设计模式之观察者模式
Table of Contents
What #
观察者模式是一种设计模式,通常用于解耦观察者与被观察者。
观察者模式中,被观察者称为主题。主题与观察者通常是1对多的关系。
观察者需要获取主题的变化。在观察者模式中,往往采用主题向观察者push的方式来传递数据。
Why #
在未考虑设计模式/原则的代码中,实现上述功能可以简述为:
package main
type Observer1 struct{}
func (n Observer1) Update(msg string) {
// do something
}
type Observer2 struct{}
func (n Observer2) Update(msg string) {
// do something
}
type Observer3 struct{}
func (n Observer3) Update(msg string) {
// do something
}
type Topic struct {
msg string
}
func (t *Topic) Notify() {
Observer1{}.Update(t.msg)
Observer2{}.Update(t.msg)
Observer3{}.Update(t.msg)
}
我们创建了三个观察者,当Topic需要向观察者发送数据时,需要实例化这三个观察者。
如果我们需要再新增一个观察者,那么Topic的Notify方法中需要再实例化这个新的观察者。
Topic和观察者耦合在了一起。
How #
通过将观察者抽象为接口,可以实现Topic和观察者之间的解耦。
package main
type Observer interface {
Update(msg string)
}
type Observer1 struct{}
func (n Observer1) Update(msg string) {
// do something
}
type Observer2 struct{}
func (n Observer2) Update(msg string) {
// do something
}
type Observer3 struct{}
func (n Observer3) Update(msg string) {
// do something
}
type Topic struct {
msg string
observers []Observer
}
func (t *Topic) Notify() {
for _, observer := range t.observers {
observer.Update(t.msg)
}
}
可以看到,Topic中的Notify方法没有再实例化观察者,而是遍历当前已注册的观察者。
当我们需要新增观察者的时候,通过Topic的注册接口添加到observers即可。注销同理。
Example #
理论总是很简单,实践往往复杂很多。
antlr中的错误监听 #
antlr是一个开源的语法解析工具。在语法解析过程中,有可能解析失败,这时候需要处理错误。
antlr作为一个开源工具,不可能为用户提供各种错误处理方式,而通过观察者模式,antlr将错误处理的能力移交给了使用者。
定义观察者行为 #
antlr中定义了观察者的行为:
type ErrorListener interface {
SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException)
ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet)
ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet)
ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet)
}
并且实现了两个观察者:
- 默认的观察者(不处理错误的观察者)。
- 将语法错误输出到终端的观察者。
type DefaultErrorListener struct {
}
func NewDefaultErrorListener() *DefaultErrorListener {
return new(DefaultErrorListener)
}
func (d *DefaultErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) {
}
func (d *DefaultErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) {
}
func (d *DefaultErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) {
}
func (d *DefaultErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) {
}
type ConsoleErrorListener struct {
*DefaultErrorListener
}
func NewConsoleErrorListener() *ConsoleErrorListener {
return new(ConsoleErrorListener)
}
func (c *ConsoleErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) {
fmt.Fprintln(os.Stderr, "line "+strconv.Itoa(line)+":"+strconv.Itoa(column)+" "+msg)
}
自定义观察者 #
作为antlr的用户,我需要自己捕获语法错误,因此,我实现了自己的错误监听器:
type ErrListener struct {
antlr.DefaultErrorListener
errList []string
}
func (el *ErrListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int,
msg string, e antlr.RecognitionException) {
el.errList = append(el.errList, fmt.Sprintf("pos: %d:%d, msg: %s", line, column, msg))
}
antlr的语法解析器提供了注册错误监听器的方法,所以在初始化解析器的时候将ErrListener注册进去即可:
// parser中有一个观察者数组:listeners []ErrorListener
parser := NewParser()
parser.AddErrorListener(&ErrListener{})
在实际解析到错误时,antlr会创建一个代理来将错误信息分发给观察者。实现如下:
func (p *BaseParser) NotifyErrorListeners(msg string, offendingToken Token, err RecognitionException) {
p._SyntaxErrors++
line := offendingToken.GetLine()
column := offendingToken.GetColumn()
listener := p.GetErrorListenerDispatch()
listener.SyntaxError(p, offendingToken, line, column, msg, err)
}
func (b *BaseRecognizer) GetErrorListenerDispatch() ErrorListener {
return NewProxyErrorListener(b.listeners)
}
type ProxyErrorListener struct {
*DefaultErrorListener
delegates []ErrorListener
}
func NewProxyErrorListener(delegates []ErrorListener) *ProxyErrorListener {
if delegates == nil {
panic("delegates is not provided")
}
l := new(ProxyErrorListener)
l.delegates = delegates
return l
}
func (p *ProxyErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) {
for _, d := range p.delegates {
d.SyntaxError(recognizer, offendingSymbol, line, column, msg, e)
}
}
func (p *ProxyErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) {
for _, d := range p.delegates {
d.ReportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs)
}
}
func (p *ProxyErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) {
for _, d := range p.delegates {
d.ReportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs)
}
}
func (p *ProxyErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) {
for _, d := range p.delegates {
d.ReportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs)
}
}
小结 #
从antlr处理错误的代码中,我们可以学到:
- 观察者模式提供了一种能力,让用户参与到行为(主题)的捕获与处理。
- 通过工厂的方式来创建了一个代理来向观察者发送数据,进一步解耦了实体(解析器)与观察者。
- 可以通过定义一个无作为的观察者(DefaultErrorListener),方便用户实现观察者。