Để tôi cung cấp cho bạn một ví dụ về cách sử dụng đơn vị XPObserver. Đầu tiên nhiều giao diện để mô phỏng một mô hình dữ liệu:
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
chỉ kết thúc tốt đẹp một giá trị byte
, nó có phương pháp để trả về giá trị và thay đổi một cách ngẫu nhiên nó. Nó cũng được quan sát bởi những người triển khai giao diện IColorChannelObserver
tự đăng ký với nó.
IColor
chỉ cần kết thúc giá trị TColor
, nó chỉ có phương thức trả về giá trị. Nó cũng có thể được quan sát bởi những người triển khai giao diện IColorObserver
tự đăng ký với giao diện đó.
Một lớp thực hiện IColorChannel
, không có gì khó khăn về nó:
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;
Bây giờ một lớp học thực hiện IColor
cho RGB, mà sẽ chứa và quan sát ba trường hợp của TColorChannel
- tức là đơn quan sát nhiều đối tượng liên quan:
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;
Nếu một trong ba giá trị kênh thay đổi, màu sắc sẽ áp dụng thay đổi và lần lượt thông báo cho tất cả các quan sát viên của nó.
Bây giờ một module dữ liệu sử dụng các lớp:
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;
Và cuối cùng là một hình thức sử dụng mà module dữ liệu và chỉ biết về các giao diện, không có gì về các lớp thực hiện:
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;
Dạng triển khai giao diện nhưng không được tính tham chiếu. Nó đăng ký chính nó để quan sát từng thuộc tính của mô-đun dữ liệu, bất cứ khi nào thay đổi kênh màu cho thấy giá trị trong ngăn trạng thái, khi màu thay đổi nó sẽ cập nhật màu nền của chính nó. Có các nút để thay đổi ngẫu nhiên các kênh màu.
Có thể có nhiều người quan sát hơn đối với các thuộc tính mô-đun dữ liệu và các phương tiện thay đổi dữ liệu khác.
Được thử nghiệm trong cả Delphi 5 và Delphi 2009 bằng FastMM4, không có rò rỉ bộ nhớ. Sẽ có rò rỉ khi không có cuộc gọi phù hợp với số DeleteObserver()
cho mỗi AddObserver()
trong biểu mẫu.