观察者模式 其实就相当于 出版者+订阅者 的关系,当有新闻时,订阅者就会得到来自出版者那边的报纸;当然,若不想订阅报纸,取消订阅即可,之后出版者也就不会再发报纸给你;反之,若想订阅报纸,注册为订阅者即可。
观察者模式的定义:观察者模式定义了对象之间的一对多依赖关系【主题(出版者)与观察者(订阅者)】,观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知,根据通知的风格,观察者可能因此新值而更新。下面稍微解析一下定义:
主题与观察者之间的一对多关系:利用观察者模式,主题是有状态的对象,并且可以控制这些状态,也就是说,有“一个”具有状态的主题;另一方面,观察者使用这些状态,虽然这些状态并不属于它们。有许多观察者依赖主题来告诉他们状态何时改变了,因此得到更新,这就产生了一对多的关系。
因为主题是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新,这也比起让许多对象控制同一份数据来,可以得到更干净的OO设计。
涉及到的OO设计原则:为了交互对象之间的松耦合而努力。
下面看看观察者模式是如何采用上面的设计原则:
松耦合即两个对象之间仍然可以交互,但是不太清楚彼此的细节。关于观察者的一切,主题只在乎观察者实现了某个接口,并不需要知道观察者的具体类是谁,做了什么或其他细节。当有新类型的观察者出现时,主题的代码不需要修改,要做的就是在新类中实现观察者接口,然后注册为观察者即可。改变主题或观察者其中一方,并不会影响另一方,因为两者是松耦合的,只要他们之间的接口仍然被遵守,我们就可以自由地改变他们。
delphi代码示例:
主题代码:
unit unt_Obserable; {$WARN SYMBOL_PLATFORM OFF} interface uses Windows, ActiveX, Classes, ComObj, Project2_TLB, StdVcl; type TObserable = class(TTypedComObject, IObserable) protected function Notice: HResult; stdcall; function Resister(const Value: IObserver): HResult; stdcall; function Initial: HResult; stdcall; function Set_Subject(const Value: WideString): HResult; stdcall; {Declare IObserable methods here} private bChange: Boolean; iIndex: Integer; pArray: array of IObserver; m_Subject: string; end; implementation uses ComServ; function TObserable.Initial: HResult; begin iIndex := 0; end; function TObserable.Resister(const Value: IObserver): HResult; begin Inc(iIndex); SetLength(pArray, iIndex); pArray[iIndex - 1] := Value; end; {$J+} function TObserable.Set_Subject(const Value: WideString): HResult; const sSubject: string = ''; begin if sSubject = Value then begin bChange := False; end else begin m_Subject := Value; sSubject := Value; bChange := True; end; end; {$J-} function TObserable.Notice: HResult; var i: Integer; begin if not bChange then Exit; for i := 0 to Length(pArray) - 1 do begin pArray[i].Update(m_Subject); end; end; initialization TTypedComObjectFactory.Create(ComServer, TObserable, Class_Obserable, ciMultiInstance, tmApartment); end.
观察者代码:
unit unt_Observer; {$WARN SYMBOL_PLATFORM OFF} interface uses Windows, ActiveX, Classes, ComObj, Project2_TLB, StdVcl; type TObserver = class(TTypedComObject, IObserver) protected procedure Update(const Value: WideString); safecall; procedure Initial(const Value: WideString); safecall; procedure Set_Observable(const Value: IObserable); safecall; private pObserable: IObserable; sMsg: string; public function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount: Integer; LocaleID: Integer; DispIDs: Pointer): HRESULT;stdcall; function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall; function GetTypeInfo(Index: Integer; LocaleID: Integer;out TypeInfo): HRESULT; stdcall; function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer; ArgErr: Pointer): HRESULT; stdcall; {Declare IObserer methods here} end; implementation uses ComServ, unt_Comm; { TObserable } function TObserver.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT; begin end; function TObserver.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HRESULT; begin end; function TObserver.GetTypeInfoCount(out Count: Integer): HRESULT; begin end; procedure TObserver.Initial(const Value: WideString); begin sMsg := Value; end; function TObserver.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,ArgErr: Pointer): HRESULT; begin end; procedure TObserver.Set_Observable(const Value: IObserable); begin pObserable := Value; pObserable.Resister(Self); end; procedure TObserver.Update(const Value: WideString); begin InfoDlg(sMsg + '值变成【' + Value + '】了...'); end; initialization TTypedComObjectFactory.Create(ComServer, TObserver, CLASS_TObserver, ciMultiInstance, tmApartment); end. 测试代码: procedure TForm1.btn1Click(Sender: TObject); var pObserable: IObserable; pOberver: IObserver; begin pObserable := CoObserable.Create; try pObserable.Initial; pOberver := CoTObserver.Create; pOberver.Initial('我是第一个观察者.'); pOberver.Set_Observable(pObserable); pOberver := CoTObserver.Create; pOberver.Initial('我是第二个观察者.'); pOberver.Set_Observable(pObserable); pOberver := CoTObserver.Create; pOberver.Initial('我是第三个观察者.'); pOberver.Set_Observable(pObserable); pObserable.Set_Subject(edt1.Text); pObserable.Notice; finally pObserable := nil; pOberver := nil; end; end;
观察者模式不是很难理解,所以代码的注释几乎没有,上面有一些方法只是定义了一下,而没有具体实现,那是Delphi语言的事,可以忽略。。。