65.9K
CodeProject 正在变化。 阅读更多。
Home

在 VB.NET 中实现观察者模式

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.94/5 (15投票s)

2004 年 1 月 2 日

3分钟阅读

viewsIcon

93659

downloadIcon

544

本文展示了一种在 VB.NET 中实现观察者模式的简单方法

解开观察者模式的神秘面纱

当您需要同时以几种不同的形式呈现数据时,观察者模式非常有用。 观察者旨在为您提供一种定义对象之间一对多依赖关系的方法,以便当一个对象的状态发生变化时,其所有依赖项都会被自动通知和更新。 包含数据的对象与显示数据的对象分离,而显示对象会观察该数据中的更改。

基本上有两种不同类型的对象:主题和观察者。 主题对应于您要跟踪其状态的数据对象。 观察者是关注主题数据更改的对象。 要设置该模式,主题类实现一种用于注册观察者以及将它们附加和分离到集合对象的方法。

您还需要一个 Notify(...) 方法,以便在数据发生更改时通知所有已注册的观察者。 对于观察者,您定义一个自定义的抽象 Observer 类以向客户端提供统一的接口,并对实现细节进行子类化。

基本实现

首先,我们定义一个名为 Observer 的公共接口

Public Interface Observer
  Sub Update(ByVal subj As Object)
End Interface

我们所有的观察者(即对数据更改感兴趣的对象)都必须实现此接口。

然后我们定义一个抽象的(在 VB.NET 中为 MustInherit)类,名为 Subject。 这是我们将要观察其状态的所有对象的基类。

Public MustInherit Class Subject

一个我们要存储观察者的集合。

Private _observers As New ArrayList()

用于添加和删除观察者的方法

  Public Sub AddObserver(ByRef ob As Observer)
    _observers.Add(ob)
  End Sub
  
  Public Sub RemoveObserver(ByRef ob As Observer)
    _observers.Remove(ob)
  End Sub

最后,一个公共方法将通知我们所有的观察者,主题的状态已经改变。

  Public Sub Notify()
    Dim ob As Observer
 
    For Each ob In _observers
      ob.Update(Me)
    Next
  End Sub
End Class

我们已经完成了实现的抽象部分。

实时示例

我们将实现 3 个类(其中一个仅用于方便)。 第一个类是 Broker

' Class Broker

' one of the model's participants

 
Public Class Broker
  Private _name As String
  Private _balance As Decimal
  Private _acc As New BrokerAccount(Me)
 
  Public ReadOnly Property Account() As BrokerAccount
    Get
      Return _acc
    End Get
  End Property
  Public Sub New()
 
  End Sub
 
  Public Property Name() As String
    Get
      Return _name
    End Get
    Set(ByVal Value As String)
      _name = Value
    End Set
  End Property
 
  Public Property Balance() As Decimal
    Get
      Return _balance
    End Get
    Set(ByVal Value As Decimal)
      _balance = Value
    End Set
  End Property
End Class 'Broker

第二个类是 BrokerAccount – 一个负责更改 Broker 余额的类。

Public Class BrokerAccount
  Inherits Subject
  Private _br As Broker
 
  Public Class InvalidBalanceException
    Inherits Exception
 
    Public Sub New()
      MyBase.New("Broker balance is less than debited amount!")
    End Sub
  End Class
 
  Public Sub New(ByRef br As Broker)
    _br = br
  End Sub
 
  Public Sub Credit(ByVal adAmount As Decimal)
    _br.Balance += adAmount
    MyBase.Notify()
  End Sub
 
  Public Sub Debit(ByVal adAmount As Decimal)
    If _br.Balance - adAmount < 0 Then
      Throw New InvalidBalanceException()
    Else
      _br.Balance -= adAmount
      MyBase.Notify()
    End If
  End Sub
End Class 'BrokerAccount

和一个辅助类 - Broker 对象的集合。

Public Class BrokerCollection
  Inherits ArrayList
 
  Default Public Overrides Property Item(ByVal index As Integer) As Object
    Get
      Return MyBase.Item(index)
    End Get
    Set(ByVal Value As Object)
      If Not TypeOf Value Is Broker Then
        Throw New Exception("Can't hold objects of other than Broker type")
      Else
        MyBase.Item(index) = Value
      End If
    End Set
  End Property
 
  Public Overrides Function Add(ByVal value As Object) As Integer
    If Not TypeOf value Is Broker Then
      Throw New Exception("Can't hold objects of other than Broker type")
    Else
      Return MyBase.Add(value)
    End If
  End Function
End Class

提供的示例 (MV_Form) 让你快速了解如何在 Windows Forms 应用程序中使用此模式

Public Class Form1
  Inherits System.Windows.Forms.Form
  Implements MV_Objects.Observer

Form_Load 事件:在这里,我们初始化一个 ListView 控件,其中包含 10 个经纪人的列表。 每个列表视图都通过其 Tag 属性(确实非常有用:))与一个 Broker 对象关联

   For i = 1 To 10
      br = New Broker()
      br.Name = "broker " & i
      br.Account.AddObserver(Me)
 
      itm = New ListViewItem(New String() {br.Name, br.Balance})
      itm.Tag = br
 
      lvBr.Items.Add(itm)
      _brcol.Add(br)
    Next

以下 sub 实现了 Observer 接口的 Update 方法。 它找到了与 ListView 中的某个经纪人关联的条目,并更新了 balance 列。

Public Sub UpdateBrokers(ByVal subj As Object) _ 
             Implements MV_Objects.Observer.Update
    Dim br As Broker, itm As ListViewItem
 
    For Each itm In lvBr.Items
      br = itm.Tag
 
      If br.Account Is subj Then
        itm.SubItems(1).Text = br.Balance
      End If
    Next
  End Sub

表单上还有 2 个按钮和一个文本框。 它们的实现很简单,为了具体起见,本文中没有介绍。

结束

就这样! 运行随附的示例应用程序以查看其工作原理。 您可以在 BrokerAccount 类的 CreditDebit 方法上放置一个断点,并使用调试器逐步执行。 这确实是一个简单但强大的模式,可以用于更多目的。 欢迎您在论坛中提及它们 :)

还可以添加到这个小型项目中的另一件事是将每个更新过程放在一个单独的线程中。 当被监视的对象数量很少时,这没问题,但在其他情况下会失败。 对于这个问题(当对象数量超过 50,000 个时),欢迎提出任何提示。

致谢

本文基于 James Maioriello 博士的工作,他在其中描述了基本的设计模式。 抱歉,我不记得是从哪里获得的,但无论如何,我确实承认了这一点 :)

© . All rights reserved.