Delphi 如何使用dunit';在DUnitWizard中实现观察者模式,甚至MVC模式?
由于Stackoverflow的明智问答,Delphi中有一些观察者模式的好例子,例如和。从这些问题中,提取了以下指导性材料链接: 在第二个stackoverflow问题中,mghie将dunit的DUnitWizard的XPObserver.pas描述为非常有趣,而其他的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中,
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;