본문 바로가기
Programming/Go

고 채널을 이용해서 옵저버 패턴 구현해보기

by TinKerBellBass 2022. 4. 5.
728x90
반응형

고 채널을 접하고 앵귤러로 프로젝트할 때의 rxjs 를 떠올렸고

pub / sub 패턴, 팬아웃 패턴에 딱이겠다고 생각했다.

그래서 정말 간단히 옵저버 패턴을 구현해 보았다.

아직 고 내공이 깊지 않아서 정말 간단히 만들었는데

좋은 의견 있으신 분들 댓글로 알려주시면 감사하겠습니다.

 

Publisher Interface

메시지를 출판하는 struct 가 구현해야 하는 메서드를 정의한 인터페이스.

구독을 신청하는 옵저버를 파라미터로 받는 Subscribe 메서드,

구독을 해지하는 옵저버를 파라미터로 받는 Unsubscribe 메서드,

 메시지를 출판하는 Notify 메서드로 이루어져 있다.

type Publisher interface {
	Subscribe(observer Observer)
	Unsubscribe(observer Observer)
	Publish(message string)
}

 

Publisher Interface 구현체

구독할 옵저버 슬라이스를 필드로 가지며, Publisher Interface 메서드들을 구현한 비상(재난) 알림 구조체.

Subscribe 메서드는 옵저버를 등록하고 채널에 값이 들어오면 처리할 리스너를 고루틴으로 활성화.

Unsubscribe 메서드는 옵저버의 이름으로 파라미터로 들어온 옵저버를 해지, 슬라이스에 remove 메소드 있으면 좋겠는데..

Publish 메소드는 등록된 옵저버에게 메시지를 전파.

type Emergency struct {
	observers []Observer
}

func NewEmergency() *Emergency {
	return &Emergency{}
}

func (e *Emergency) Subscribe(observer Observer) {
	e.observers = append(e.observers, observer)
	go observer.ListenOnChannel()
}

func (e *Emergency) Unsubscribe(observer Observer) {
	for i, o := range e.observers {
		if o.GetName() == observer.GetName() {
			e.observers = append(e.observers[:i], e.observers[i+1:]...)
			return
		}
	}
}

func (e *Emergency) Publish(message string) {
	for _, o := range e.observers {
		o.Receiver(message)
	}
}

 

 

Observer Interface

메시지를 구독하는 옵저버 인터페이스.

메시지를 받는 Receiver 메서드,

옵저버의 이름을 얻을 수 있는 GetName 메서드,

채널에 값이 들어오나 감시하는 ListenOnChannel 메서드로

이루어져 있다. 

type Observer interface {
	Receiver(message string)
	GetName() string
	ListenOnChannel()
}

 

Observer Interface  구현체

메시지가 이동할 통로인 채널과 이름을 가진 개인 구독자 구현체.

Receiver 메서드는 메시지를 받아서 채널에 송신.

ListenOnChannel 메소드는 채널에서 값을 수신하여 처리.

type PersonalSubscriber struct {
	ch   chan string
	name string
}

func NewPersonalSubscriber(name string) *PersonalSubscriber {
	return &PersonalSubscriber{
		ch:   make(chan string),
		name: name,
	}
}

func (p *PersonalSubscriber) Receiver(message string) {
	p.ch <- message
}

func (p *PersonalSubscriber) ListenOnChannel() {
	for msg := range p.ch {
		fmt.Printf("%s님 %s이(가) 발생했어요. 가까운 피난소로 대피하거나, 119에 전화하세요!\n", p.name, msg)
	}
}

func (p *PersonalSubscriber) GetName() string {
	return p.name
}

 

Main

companySubscriber 는 생략.

비상(재난) 알림 구현체를 만들고

개인 구독자와 회사 구독자를 등록한 뒤,

재난 알림 메시지를 전송.

테스트해보니 이슈가 있었는데 내공이 깊어지면 해결해 볼 생각 중.

done 채널 패턴, wait 다 해봤는데 안돼서 일단 포기.

전체 소스는 https://github.com/Jongwon-Hyun/observer_with_channel 

func main() {
	emergency := pubSub.NewEmergency()
	personalSub := pubSub.NewPersonalSubscriber("팅커벨")
	companySub := pubSub.NewCompanySubscriber("네버랜드")

	emergency.Subscribe(personalSub)
	emergency.Subscribe(companySub)
	emergency.Publish("태풍")
	emergency.Publish("화재")

	emergency.Unsubscribe(personalSub)

	emergency.Publish("산사태")
	emergency.Publish("해일")

	/*
		이슈: Publish 가 짝수일 경우만 제대로 동작함
		원인 : 모르겠음...
		조금 더 고 내공이 쌓이면 해결해 볼 생각중
	*/
}

 

728x90
반응형

댓글