gpt4 book ai didi

delphi - 如何使用dunit的DUnitWizard中包含的XPObserver单元来实现观察者模式,甚至MVC模式?

转载 作者:行者123 更新时间:2023-12-03 15:02:30 24 4
gpt4 key购买 nike

Delphi 中有很好的观察者模式示例,这要归功于 Stackoverflow 上的明智问答,例如 Best way to implement observer pattern in DelphiAre there any Videos/Screen casts or other resources on how to use Interfaces in Delphi? 。从这些 stackoverflow 问题中,提取了以下指导 Material 的链接:

  1. Joanna Carter's blog

  2. SourceMaking site

  3. TDelphiHobbyist's blog

  4. itte.no site

  5. dunit's DUnitWizard

在第二个 stackoverflow 问题中,mghie 描述了 dunit's DUnitWizard's XPObserver.pas非常有趣和其他XP*.pas值得仔细观察。然而,XPObserver单位仅在两个地方被引用,即 dunit\Contrib\DUnitWizard\Source\Common\dunit\XPObserverTests.pas其中测试的唯一兴趣似乎是检查引用计数,并且 dunit\Contrib\DUnitWizard\Source\DelphiExperts\DUnitProject\XPTestedUnitUtils.pas其中仅使用 XPObserver 单元中声明的 IXPFamily 类型。

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

例如:设计问题,例如:

(1) 如何使用XPObserver实现观察者模式做某事的单元?

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

或者编码问题,例如:

(3) XPObserverTXPSubjects据称能够提供启用 single observer<->multiple subject 的功能关系。然而,FSubjects被宣布为私有(private)。也没有 setter/getter 。我想知道这是设计使然吗? (例如,作者在 // ...***DON'T*** refactor this method!! 中写了 TXPSubject.DeleteObserver 。因此,我没有信心修改代码,因为我无法完全理解这一点,也许还有其他部分。)如果是这样,使用 TXPSubjects 启用的假设方法是什么single observer<->multiple subject关系?

非常感谢您的宝贵时间和评论!

最佳答案

让我给您举一个如何使用 XPObserver 单元的例子。首先模拟数据模型的几个接口(interface):

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 只是包装一个 byte 值,它有方法返回该值并随机更改它。向其注册的 IColorChannelObserver 接口(interface)的实现者也可以观察到它。

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

一个实现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;

现在是一个实现 RGB 的 IColor 的类,它将包含并观察 TColorChannel 的三个实例 - 即单个观察者多个主题关系:

type
TRGBColor = class(TXPSubject, IColor, IColorChannelObserver)
function GetValue: TColor;
private
fRed: IColorChannel;
fGreen: IColorChannel;
fBlue: IColorChannel;
fValue: TColor;
function InternalUpdate: boolean;
public
constructor Create(ARed, AGreen, ABlue: IColorChannel);

procedure Changed(const AChannel: IColorChannel);
end;

constructor TRGBColor.Create(ARed, AGreen, ABlue: IColorChannel);
begin
Assert(ARed <> nil);
Assert(AGreen <> nil);
Assert(ABlue <> nil);
inherited Create;
fRed := ARed;
fRed.AddObserver(Self, fRed);
fGreen := AGreen;
fGreen.AddObserver(Self, fGreen);
fBlue := ABlue;
fBlue.AddObserver(Self, fBlue);
InternalUpdate;
end;

procedure TRGBColor.Changed(const AChannel: IColorChannel);
var
Idx: integer;
Ico: IColorObserver;
begin
if InternalUpdate then
for Idx := 0 to ObserverCount - 1 do begin
if GetObserver(Idx).QueryInterface(IColorObserver, Ico) = S_OK then
Ico.Changed(Self);
end;
end;

function TRGBColor.GetValue: TColor;
begin
Result := fValue;
end;

function TRGBColor.InternalUpdate: boolean;
var
Value: TColor;
begin
Result := False;
Value := RGB(fRed.GetValue, fGreen.GetValue, fBlue.GetValue);
if fValue <> Value then begin
fValue := Value;
Result := True;
end;
end;

如果三个 channel 值中的任何一个发生更改,颜色将应用更改并依次通知其所有观察者。

现在是使用这些类的数据模块:

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;

最后是一个使用该数据模块并且只知道接口(interface)的表单,而不知道实现类:

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;

该表单实现了接口(interface),但不是引用计数的。它注册自身以观察数据模块的四个属性中的每一个,每当颜色 channel 发生变化时,它就会在状态栏 Pane 中显示值,当颜色发生变化时,它会更新自己的背景颜色。有一些按钮可以随机更改颜色 channel 。

可以有更多的观察者来观察数据模块属性和其他更改数据的方法。

使用 FastMM4 在 Delphi 5 和 Delphi 2009 中进行测试,没有内存泄漏。当表单中的每个 AddObserver() 没有匹配的 DeleteObserver() 调用时,就会出现泄漏。

关于delphi - 如何使用dunit的DUnitWizard中包含的XPObserver单元来实现观察者模式,甚至MVC模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11105361/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com