Observer 패턴은 특정 이벤트나 상태가 변경되었을 경우 알림을 받고 싶을 때 사용되는 패턴입니다.
이 패턴을 구현하기 위해서는 보통 Observable(알림을 주는 객체)와 Observer(알림을 받는 객체)를 구현하게 됩니다.
흔히 아는 pub/sub 패턴으로도 많이 사용되는 패턴입니다.
예를 들어 환자와 간호사가 있는데 1명의 간호사는 여러 명의 환자들을 관리하고 간호사들은 환자의 열과 심박수를 체크합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
type Observable interface {
subscribe(observer Observer)
unsubscribe(observer Observer)
notifyAll(data interface{})
}
type Patient struct {
name string
temperature float64
heartRate int
observerList []Observer
}
func NewPatient(name string, temperature float64, heartRate int) *Patient {
return &Patient{
name,
temperature,
heartRate,
make([]Observer, 0),
}
}
func (p *Patient) subscribe(observer Observer) {
p.observerList = append(p.observerList, observer)
}
func (p *Patient) unsubscribe(observer Observer) {
removedIdx := -1
for i:=0; i<len(p.observerList); i++ {
if p.observerList[i].getId() == observer.getId() {
removedIdx = i
break
}
}
if removedIdx == -1 {
return
} else {
p.observerList[removedIdx] = p.observerList[len(p.observerList)-1]
p.observerList = p.observerList[:len(p.observerList)-1]
}
}
func (p *Patient) notifyAll(data interface{}) {
for _, observer := range p.observerList {
observer.update(data)
}
}
|
cs |
Observable 인터페이스와 해당 인터페이스를 구현한 환자 구조체입니다.
사실 이번 예제에서는 Observable 타입은 사용되지 않지만 이렇게 인터페이스를 선언하면 조금 더 유연한 코드를 작성할 수 있습니다.
unsubscribe 코드가 조금 이해가 안될 수도 있는데 Patient 구조체의 observerList에는 간호사가 들어가게 될텐데
해당 리스트에서 간호사의 순서는 상관이 없으므로 remove하려는 간호사를 뺀 후 다시 slice를 만드는 것보다는 위 코드처럼하면 훨씬 더 빠른 코드를 작성할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
|
func (p *Patient) setTemperature(temperature float64) {
p.temperature = temperature
medicalInfo := NewMedicalInfo(p.name, TEMPERATURE, p.temperature)
p.notifyAll(medicalInfo)
}
func (p *Patient) setHeartRate(heartRate int) {
p.heartRate = heartRate
medicalInfo := NewMedicalInfo(p.name, HEARTRATE, p.heartRate)
p.notifyAll(medicalInfo)
}
|
cs |
여기까지 Patient 구조체의 코드입니다. 환자의 온도와 심박수가 변한다면 간호사는 변화를 보고 어떠한 조치를 취해야하기 때문에 온도와 심박수를 set한 후에 notifyAll을 통해 알려줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
const (
TEMPERATURE = iota
HEARTRATE
)
type MedicalInfo struct {
PatientName string
Kind int
Value interface{}
}
func NewMedicalInfo(name string, kind int, value interface{}) MedicalInfo {
return MedicalInfo{
name,
kind,
value,
}
}
|
cs |
간호사 관련 코드를 보기전에 간호사가 환자로부터 받게 될 정보에 관련된 코드입니다.
iota를 이용하여 환자가 온도 관련 정보를 보냈는지 심박수 관련 정보를 보냈는지를 알 수 있고
온도는 float64 타입이고 심박수는 int 타입이기 때문에 Value는 interface{}로 정의하였습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
type Observer interface {
update(data interface{})
getId() string
}
type Nurse struct {
name string
id string
}
func NewNurse(name string) *Nurse {
id := xid.New().String()
return &Nurse{
name: name,
id: id,
}
}
func (n *Nurse) update(data interface{}) {
if medicalInfo, ok := data.(MedicalInfo); ok {
switch medicalInfo.Kind {
case TEMPERATURE:
if medicalInfo.Value.(float64) > 38 {
fmt.Printf("%s goes to %s\n", medicalInfo.PatientName, n.name)
}
case HEARTRATE:
if medicalInfo.Value.(int) < 30 {
fmt.Printf("%s calls a doctor\n", n.name)
}
}
}
}
func (n *Nurse) getId() string {
return n.id
}
|
cs |
이제 간호사 관련 구조체 코드입니다.
각 간호사를 식별하기 위해 랜덤한 id를 부여합니다.
그리고 update에서는 interface 형변환을 통해 환자가 어떤 정보를 보냈는지 파악하고, 고열이 나거나 심박수가 너무 낮으면 간호사는 어떠한 조치를 취하게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func main() {
p1 := NewPatient("bob", 36.5, 60)
p2 := NewPatient("tony", 36, 80)
n1 := NewNurse("sarah")
n2 := NewNurse("christina")
p1.subscribe(n1)
p1.subscribe(n2)
fmt.Println(p1.observerList)
p2.subscribe(n2)
p1.setTemperature(40)
p2.setHeartRate(20)
}
|
cs |
위 같은 코드를 작성하면 bob의 온도는 40도가 되어 담당 간호사들에게 알림이 가고 간호사는 조치를 취하게 됩니다.
tony 역시 심박수가 낮아져서 간호사에게 알림이 갑니다.
'golang > design pattern' 카테고리의 다른 글
golang design pattern #11 Chain of Responsibility (0) | 2021.11.16 |
---|---|
golang design pattern #10 Proxy (0) | 2021.11.15 |
golang design pattern #9 Flyweight (0) | 2021.11.14 |
golang design pattern #8 Decorator (0) | 2021.11.13 |
golang design pattern #7 Composite (0) | 2021.11.12 |