Delphi 如何使用dunit';在DUnitWizard中实现观察者模式,甚至MVC模式?

Delphi 如何使用dunit';在DUnitWizard中实现观察者模式,甚至MVC模式?,delphi,model-view-controller,oop,observer-pattern,dunit,Delphi,Model View Controller,Oop,Observer Pattern,Dunit,由于Stackoverflow的明智问答,Delphi中有一些观察者模式的好例子,例如和。从这些问题中,提取了以下指导性材料链接: 在第二个stackoverflow问题中,mghie将dunit的DUnitWizard的XPObserver.pas描述为非常有趣,而其他的XP*.pas则值得仔细观察。但是,XPObserver单元仅在两个位置被引用,即dunit\Contrib\DUnitWizard\Source\Common\dunit\XPObserverTests.pas中,

由于Stackoverflow的明智问答,Delphi中有一些观察者模式的好例子,例如和。从这些问题中,提取了以下指导性材料链接:

  • 在第二个stackoverflow问题中,mghie将dunit的DUnitWizard的XPObserver.pas描述为非常有趣,而其他的
    XP*.pas
    则值得仔细观察。但是,
    XPObserver
    单元仅在两个位置被引用,即
    dunit\Contrib\DUnitWizard\Source\Common\dunit\XPObserverTests.pas
    中,测试的唯一目的似乎是检查引用计数,和
    dunit\Contrib\DUnitWizard\Source\DelphiExperts\DUnitProject\xptestedinituitils.pas
    ,其中仅使用XPObserver单元中声明的IXPFamily类型

    因此,我想知道使用这个
    XPObserver
    单元的最佳实践是什么

    例如:设计问题,例如:

    (1) 如何使用
    XPObserver
    单元来实现一个执行某些操作的观察者模式

    (2) 如何使用
    XPObserver
    实现MVC模式

    或编写问题,如:

    (3)
    XPObserver
    TXPSubjects
    声称提供启用
    单观察者多主题关系的能力。但是,
    FSubjects
    被声明为私有。也没有获得者。我想知道这是故意的吗?(例如,作者在
    TXPSubject.DeleteObserver
    中编写了
    /…***不要***重构此方法!!
    。因此,我没有信心修改代码,因为我无法完全理解此方法,也可能无法完全理解其他部分。),使用TXPSubjects启用
    单观察者多主题关系的假定方法是什么


    非常感谢您的时间和评论

    让我给你一个如何使用XPObserver单元的例子。模拟数据模型的前几个接口:

    type
      IColorChannel = interface(IXPSubject)
        function GetValue: byte;
        procedure RandomChange;
      end;
    
      IColorChannelObserver = interface(IXPObserver)
        ['{E1586F8F-32FB-4F77-ACCE-502AFDAF0EC0}']
        procedure Changed(const AChannel: IColorChannel);
      end;
    
      IColor = interface(IXPSubject)
        function GetValue: TColor;
      end;
    
      IColorObserver = interface(IXPObserver)
        ['{0E5D2FEC-5585-447B-B242-B9B57FC782F2}']
        procedure Changed(const AColor: IColor);
      end;
    
    IColorChannel
    只包装一个
    字节
    值,它有返回值和随机更改值的方法。
    IColorChannelObserver
    接口的实现者也可以观察到它,并向它注册

    IColor
    只包装一个
    TColor
    值,它只有一个返回值的方法。
    IColorObserver
    接口的实现者也可以观察到它,并向它注册

    一个实现IColorChannel的类,没有什么困难:

    type
      TColorChannel = class(TXPSubject, IColorChannel)
        function GetValue: byte;
        procedure RandomChange;
      private
        fValue: byte;
      end;
    
    function TColorChannel.GetValue: byte;
    begin
      Result := fValue;
    end;
    
    procedure TColorChannel.RandomChange;
    var
      Value, Idx: integer;
      Icco: IColorChannelObserver;
    begin
      Value := Random(256);
      if fValue <> Value then begin
        fValue := Value;
        for Idx := 0 to ObserverCount - 1 do begin
          // Or use the Supports() function instead of QueryInterface()
          if GetObserver(Idx).QueryInterface(IColorChannelObserver, Icco) = S_OK then
            Icco.Changed(Self);
        end;
      end;
    end;
    
    如果三个通道值中的任何一个发生变化,则颜色将应用该变化,并依次通知其所有观察者

    现在,一个使用以下类的数据模块:

    type
      TDataModule1 = class(TDataModule)
        procedure DataModuleCreate(Sender: TObject);
      private
        fRed: IColorChannel;
        fGreen: IColorChannel;
        fBlue: IColorChannel;
        fColor: IColor;
      public
        property BlueChannel: IColorChannel read fBlue;
        property GreenChannel: IColorChannel read fGreen;
        property RedChannel: IColorChannel read fRed;
        property Color: IColor read fColor;
      end;
    
    procedure TDataModule1.DataModuleCreate(Sender: TObject);
    begin
      Randomize;
    
      fRed := TColorChannel.Create;
      fGreen := TColorChannel.Create;
      fBlue := TColorChannel.Create;
    
      fColor := TRGBColor.Create(fRed, fGreen, fBlue);
    end;
    
    最后一个表单使用该数据模块,只知道接口,不知道实现类:

    type
      TForm1 = class(TForm, IXPObserver, IColorChannelObserver, IColorObserver)
        Button1: TButton;
        Button2: TButton;
        Button3: TButton;
        StatusBar1: TStatusBar;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure ButtonClick(Sender: TObject);
      public
        procedure Changed(const AChannel: IColorChannel); overload;
        procedure Changed(const AColor: IColor); overload;
        procedure ReleaseSubject(const Subject: IXPSubject;
          const Context: pointer);
      private
        fChannels: array[0..2] of IColorChannel;
        fColor: IColor;
      end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      Idx: integer;
    begin
      Button1.Caption := 'red';
      Button1.Tag := 0;
      fChannels[0] := DataModule1.RedChannel;
    
      Button2.Caption := 'green';
      Button2.Tag := 1;
      fChannels[1] := DataModule1.GreenChannel;
    
      Button3.Caption := 'blue';
      Button3.Tag := 2;
      fChannels[2] := DataModule1.BlueChannel;
    
      for Idx := 0 to 2 do
        fChannels[Idx].AddObserver(Self, fChannels[Idx]);
    
      fColor := DataModule1.Color;
      fColor.AddObserver(Self, fColor);
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    var
      Idx: integer;
    begin
      for Idx := Low(fChannels) to High(fChannels) do
        fChannels[Idx].DeleteObserver(Self);
      fColor.DeleteObserver(Self);
    end;
    
    procedure TForm1.ButtonClick(Sender: TObject);
    var
      Button: TButton;
    begin
      Button := Sender as TButton;
      if (Button.Tag >= Low(fChannels)) and (Button.Tag <= High(fChannels)) then
        fChannels[Button.Tag].RandomChange;
    end;
    
    procedure TForm1.Changed(const AChannel: IColorChannel);
    var
      Idx: integer;
    begin
      Assert(AChannel <> nil);
      for Idx := Low(fChannels) to High(fChannels) do
        if fChannels[Idx] = AChannel then begin
          while StatusBar1.Panels.Count <= Idx do
            StatusBar1.Panels.Add;
          StatusBar1.Panels[Idx].Text := IntToStr(AChannel.GetValue);
          break;
        end;
    end;
    
    procedure TForm1.Changed(const AColor: IColor);
    begin
      Assert(AColor <> nil);
      Color := AColor.GetValue;
    end;
    
    procedure TForm1.ReleaseSubject(const Subject: IXPSubject;
      const Context: pointer);
    var
      Idx: integer;
    begin
      // necessary if the objects implementing IXPSubject are not reference-counted
      for Idx := Low(fChannels) to High(fChannels) do begin
        if Subject = fChannels[Idx] then
          fChannels[Idx] := nil;
      end;
      if Subject = fColor then
        fColor := nil;
    end;
    
    类型
    TForm1=类(TForm、IXPObserver、IColorChannelObserver、IColorObserver)
    按钮1:t按钮;
    按钮2:t按钮;
    按钮3:t按钮;
    StatusBar1:TStatusBar;
    过程表单创建(发送方:ToObject);
    销毁程序表(发送方:TObject);
    程序按钮点击(发送方:ToObject);
    公众的
    程序更改(常数通道:IColorChannel);超载;
    程序更改(const AColor:IColor);超载;
    程序发布主体(const主体:IXPSObject;
    常量上下文:指针);
    私有的
    FCchannels:IColorChannel的数组[0..2];
    fColor:IColor;
    结束;
    过程TForm1.FormCreate(发送方:TObject);
    变量
    Idx:整数;
    开始
    按钮1.标题:=“红色”;
    按钮1.标签:=0;
    fcchannels[0]:=DataModule1.RedChannel;
    按钮2.标题:=“绿色”;
    按钮2.标签:=1;
    fcchannels[1]:=DataModule1.GreenChannel;
    按钮3.标题:=“蓝色”;
    按钮3.标签:=2;
    fcchannels[2]:=DataModule1.BlueChannel;
    对于Idx:=0到2 do
    fcchannels[Idx].AddObserver(Self,fcchannels[Idx]);
    fColor:=DataModule1.Color;
    添加观察者(Self,fColor);
    结束;
    程序TForm1.FormDestroy(发送方:ToObject);
    变量
    Idx:整数;
    开始
    对于Idx:=低(FCchannels)到高(FCchannels)do
    fcchannels[Idx].DeleteObserver(Self);
    fColor.DeleteObserver(Self);
    结束;
    程序TForm1.按钮点击(发送方:TObject);
    变量
    按钮:t按钮;
    开始
    按钮:=发送器作为TButton;
    如果(Button.Tag>=低(fcchannels))和(Button.Tag
    
    type
      TForm1 = class(TForm, IXPObserver, IColorChannelObserver, IColorObserver)
        Button1: TButton;
        Button2: TButton;
        Button3: TButton;
        StatusBar1: TStatusBar;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure ButtonClick(Sender: TObject);
      public
        procedure Changed(const AChannel: IColorChannel); overload;
        procedure Changed(const AColor: IColor); overload;
        procedure ReleaseSubject(const Subject: IXPSubject;
          const Context: pointer);
      private
        fChannels: array[0..2] of IColorChannel;
        fColor: IColor;
      end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      Idx: integer;
    begin
      Button1.Caption := 'red';
      Button1.Tag := 0;
      fChannels[0] := DataModule1.RedChannel;
    
      Button2.Caption := 'green';
      Button2.Tag := 1;
      fChannels[1] := DataModule1.GreenChannel;
    
      Button3.Caption := 'blue';
      Button3.Tag := 2;
      fChannels[2] := DataModule1.BlueChannel;
    
      for Idx := 0 to 2 do
        fChannels[Idx].AddObserver(Self, fChannels[Idx]);
    
      fColor := DataModule1.Color;
      fColor.AddObserver(Self, fColor);
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    var
      Idx: integer;
    begin
      for Idx := Low(fChannels) to High(fChannels) do
        fChannels[Idx].DeleteObserver(Self);
      fColor.DeleteObserver(Self);
    end;
    
    procedure TForm1.ButtonClick(Sender: TObject);
    var
      Button: TButton;
    begin
      Button := Sender as TButton;
      if (Button.Tag >= Low(fChannels)) and (Button.Tag <= High(fChannels)) then
        fChannels[Button.Tag].RandomChange;
    end;
    
    procedure TForm1.Changed(const AChannel: IColorChannel);
    var
      Idx: integer;
    begin
      Assert(AChannel <> nil);
      for Idx := Low(fChannels) to High(fChannels) do
        if fChannels[Idx] = AChannel then begin
          while StatusBar1.Panels.Count <= Idx do
            StatusBar1.Panels.Add;
          StatusBar1.Panels[Idx].Text := IntToStr(AChannel.GetValue);
          break;
        end;
    end;
    
    procedure TForm1.Changed(const AColor: IColor);
    begin
      Assert(AColor <> nil);
      Color := AColor.GetValue;
    end;
    
    procedure TForm1.ReleaseSubject(const Subject: IXPSubject;
      const Context: pointer);
    var
      Idx: integer;
    begin
      // necessary if the objects implementing IXPSubject are not reference-counted
      for Idx := Low(fChannels) to High(fChannels) do begin
        if Subject = fChannels[Idx] then
          fChannels[Idx] := nil;
      end;
      if Subject = fColor then
        fColor := nil;
    end;