Design Patterns(二十):Observer Pattern--VB代码
★★★★★
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
结构图

角色
- 抽象主题(Subject)角色:主题角色把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色。
- 抽象观察者(ConcreteSubject)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。
- 具体主题(Observer)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。
- 具体观察者(ConcreteObserver)角色:存储与主题的状态自洽的状态。具体观察者角色事项抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题对象的引用。
动机
在软件构建过程中集合,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。
意图
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
示意性代码


'MainApp test application
Module MainApp
Public Sub Main()
'Configure Observer pattern
Dim s As ConcreteSubject = New ConcreteSubject()
s.Attach(New ConcreteObserver(s, "X"))
s.Attach(New ConcreteObserver(s, "Y"))
s.Attach(New ConcreteObserver(s, "Z"))
'Change subject and notify observers
s.SubjectState = "ABC"
s.Notify()
'Wait for user
Console.ReadLine()
End Sub
End Module
' "Subject"
Public MustInherit Class Subject
Private observers As New List(Of Observer)
Public Sub Attach(ByVal observer As Observer)
observers.Add(observer)
End Sub
Public Sub Detach(ByVal observer As Observer)
observers.Remove(observer)
End Sub
Public Sub Notify()
For Each o As Observer In observers
o.update()
Next
End Sub
End Class
'"ConcreteSubject"
Public Class ConcreteSubject
Inherits Subject
Private _subjectState As String
'Property
Public Property SubjectState() As String
Get
Return _subjectState
End Get
Set(ByVal value As String)
_subjectState = value
End Set
End Property
End Class
'"Observer"
Public MustInherit Class Observer
MustOverride Sub update()
End Class
'"ConcreteObserver"
Public Class ConcreteObserver
Inherits Observer
Private _name As String
Private observerState As String
Private _subject As ConcreteSubject
'Constructor
Public Sub New(ByVal subject As ConcreteSubject, ByVal name As String)
Me._subject = subject
Me._name = name
End Sub
'Property
Public Property Subject() As ConcreteSubject
Get
Return _subject
End Get
Set(ByVal value As ConcreteSubject)
_subject = value
End Set
End Property
Public Overrides Sub Update()
observerState = _subject.SubjectState
Console.WriteLine("Observer {0}'s new state is {1}", _
_name, observerState)
End Sub
End Class
一个实例
下面的观察者代码演示了投资者登记后,每次股价发生变化,都会得到通知。


Module MainApp
Public Sub MainApp()
'Create investors
Dim s As New Investor("Sorros")
Dim b As New Investor("Berkshire")
'Create IBM stock and attach investors
Dim ibm As New IBM("IBM", 120.0)
ibm.Attach(s)
ibm.Attach(b)
'Change price, which notifies investors
ibm.Price = 121.1
ibm.Price = 122.0
ibm.Price = 123.5
ibm.Price = 124.75
'Wait for user
Console.ReadLine()
End Sub
End Module
'"Subject"
Public MustInherit Class Stock
Private _Price As Double
Private _Symbol As String
Private investors As New List(Of Investor)
'Constructor
Sub New(ByVal symbol As String, ByVal price As Double)
_Symbol = symbol
_Price = price
End Sub
'Properties
Public Property Price() As Double
Get
Return _Price
End Get
Set(ByVal value As Double)
_Price = value
Notify()
End Set
End Property
Public Property Symbol() As String
Get
Return _Symbol
End Get
Set(ByVal value As String)
_Symbol = value
End Set
End Property
Public Sub Attach(ByVal investor As Investor)
investors.Add(investor)
End Sub
Public Sub Detach(ByVal investor As Investor)
investors.Remove(investor)
End Sub
Public Sub Notify()
For Each investor As Investor In investors
investor.Update(Me)
Next
Console.WriteLine("")
End Sub
End Class
'"ConcreteObject"
Public Class IBM
Inherits Stock
'Constructor
Sub New(ByVal symbol As String, ByVal price As Double)
MyBase.New(symbol, price)
End Sub
End Class
'"Observer"
Public Interface IInvestor
Sub Update(ByVal stock As Stock)
End Interface
'"ConcreteObserver"
Public Class Investor
Implements IInvestor
Private _Stock As Stock
Private name As String
'Constructor
Sub New(ByVal name As String)
Me.name = name
End Sub
Public Property Stock() As Stock
Get
Return _Stock
End Get
Set(ByVal value As Stock)
_Stock = value
End Set
End Property
Public Sub Update(ByVal stock As Stock) Implements IInvestor.Update
Console.WriteLine("Notified {0} of {1}'s " & "change to {2:C}", _
name, stock.Symbol, stock.Price)
End Sub
End Class
改进后的代码
用.Net的事件委托实现上面的实例


Module MainApp
Public Sub Main()
'Create investors
Dim s As New Investor("Sorros")
Dim b As New Investor("Berkshire")
'Create IBM stock and attach investors
Dim ibm As New IBM("IBM", 120.0)
ibm.Attach(s)
ibm.Attach(b)
'Change price, which notifies investors
ibm.Price = 121.1
ibm.Price = 122.0
ibm.Price = 123.5
ibm.Price = 124.75
'Wait for user
Console.ReadLine()
End Sub
End Module
'"Subject"
Public MustInherit Class Stock
Private _Price As Double
Private _Symbol As String
Private Event InvesteEvent(ByVal symbol As String, ByVal price As Double)
'Constructor
Sub New(ByVal symbol As String, ByVal price As Double)
_Symbol = symbol
_Price = price
End Sub
'Properties
Public Property Price() As Double
Get
Return _Price
End Get
Set(ByVal value As Double)
_Price = value
Notify()
End Set
End Property
Public Property Symbol() As String
Get
Return _Symbol
End Get
Set(ByVal value As String)
_Symbol = value
End Set
End Property
Public Sub Attach(ByVal investor As IInvestor)
AddHandler InvesteEvent,AddressOf investor.Update
End Sub
Public Sub Detach(ByVal investor As IInvestor)
RemoveHandler InvesteEvent,AddressOf investor.Update
End Sub
Public Sub Notify()
OnInveste
Console.WriteLine("")
End Sub
Protected Sub OnInveste()
RaiseEvent InvesteEvent(_symbol,_Price)
End Sub
End Class
'"ConcreteSbject"
Public Class IBM
Inherits Stock
'Constructor
Sub New(ByVal symbol As String, ByVal price As Double)
MyBase.New(symbol, price)
End Sub
End Class
'"Observer"
Public Interface IInvestor
Sub Update(ByVal symbol As String, ByVal price As Double)
End Interface
'"ConcreteObserver"
Public Class Investor
Private name As String
'Constructor
Sub New(ByVal name As String)
Me.name = name
End Sub
Public Sub Update(ByVal symbol As String, ByVal price As Double) Implements IInvestor.Update
Console.WriteLine("Notified {0} of {1}'s " & "change to {2:C}", _
name, Symbol, Price)
End Sub
End Class
Observer Pattern模式的几个要点:
1、使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
2、目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是不需要订阅通知,目标对象对此一无所知。
3、在C#的event中,委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象。委托是比抽象Observer更为松耦合的设计。
我的理解
封装对象通知,支持通信对象的变化。
参考资料
《C#面向对象设计模式纵横谈系列课程(21)》 李建中老师
分类:
Design Pattern
随笔分类 (333)
Cnblogs's
Front End
Oracle's
Software's
- Apache HTTP Server
- CodeSmith Community
- Grapecity(FAQ)
- Mybase
- ClubFarPoint(Forum)
- Beyond Compare
- CrystalReport(FAQ)
- 秀丸
- Software Advice
- Software: Business & Nonprofit | Reviews and Top Software at Capterra
- Capterra
- Business Software Reviews from Software Advice
Copyright © 2025 sekihin
Powered by .NET 9.0 on Kubernetes
Powered by .NET 9.0 on Kubernetes
![]() | 本作品采用 知识共享署名-非商业性使用 2.5 中国大陆许可协议进行许可。 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通