观察者模式
观察者模式定义了一系列对象之间的一对多关系。
当一个对象改变状态时,其他依赖者都会收到通知。
模型说明
1.设计要点
设计观察者模式的程序时要注意以下几点。 (1)要明确谁是观察者谁是被观察者。一般观察者与被观察者之间是多对一的关系,一个被观察者对象可以有多个观察者对象。 (2)被观察者对象在发送广播通知时,无须指定具体的观察者对象,观察者对象可自己决定是否订阅被观察者对象的通知。 (3)被观察者至少有三个方法:添加观察者、移除观察者、通知观察者的方法。观察者至少有一个方法:更新方法,即更新当前的内容,做出相应的处理。
2.推模型和拉模型
观察者模式根据其侧重的功能可分为推模型和拉模型。 (1)推模型 被观察者对象向观察者对象推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。一般在这种模型的实现中会将被观察者对象中的全部或部分信息通过作为观察者的更新方法的参数传递给观察者。 (2)拉模型 被观察者对象在通知观察者时,只传递少量信息。如果观察者需要更详细的信息,由观察者主动到被观察者对象中获取,相当于观察者从被观察者对象中拉取数据。
推模型被认为是更正确的做法。
优缺点
1.优点
- 开闭原则。 你无需修改被观察者代码就能引入新的观察者。
- 你可以在运行时建立对象之间的联系。
2.缺点
- 观察者的通知顺序是随机的。
使用场景
- 当一个对象的状态改变时,需要联动多个对象进行状态变更/显示时。
- 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节,如消息推送等。
参考代码
subject.go
package main type Subject interface { RegisterObserver(name string, o Observer) RemoveObserve(name string) Notify() } type PeopleSubject struct { Observers map[string]Observer Question string } func (s *PeopleSubject) RegisterObserver(name string, o Observer) { if s.Observers == nil { s.Observers = make(map[string]Observer) } s.Observers[name] = o } func (s *PeopleSubject) RemoveObserve(name string) { delete(s.Observers, name) } func (s *PeopleSubject) Notify() { for _, o := range s.Observers { o.Answer() } } func (s *PeopleSubject) SetQuestion(question string) { s.Question = question s.Notify() }
observer.go
package main import "fmt" type Observer interface { Answer() } type AngelObserver struct{} func (o *AngelObserver) Answer() { fmt.Println("fuck off") } type DemonObserver struct{} func (o *DemonObserver) Answer() { fmt.Println("go for it!") }
main.go
package main var ( // PeopleSubject 实现 Subject 接口 _ Subject = (*PeopleSubject)(nil) ) func main() { s := new(PeopleSubject) s.RegisterObserver("angle", new(AngelObserver)) s.RegisterObserver("demon", new(DemonObserver)) s.SetQuestion("what should i do?") }
output:
fuck off go for it!