示例:透明性设计
说明:
提供透明性的唯一方法是在Component中定义缺省Add和Remove操作。这又带来了一个新的问题:Component.Add的实现不可避免地会有失败的可能性。你可以不让Component.Add做任何事情,但这就忽略了一个很重要的问题:企图向叶节点中增加一些东西时可能会引入错误。这时Add操作会产生垃圾。你可以让Add操作删除它的参数,但可能客户并不希望这样。
如果该组件不允许有子部件,或者Remove的参数不是该组件的子节点时,通常最好使用缺省方式(可能是产生一个异常)处理Add和Remove的失败。
另一个办法是对“删除”的含义作一些改变。如果该组件有一个父部件引用,我们可重新定义Component.Remove,在它的父组件中删除掉这个组件。然而,对应的Add操作仍然没有合理的解释。
代码:
unit uComposite4;
interface
uses
SysUtils,Dialogs;
type
TComponent = class
protected
function GetChilds(Index: integer): TComponent; virtual;
function GetCount: integer; virtual;
public
procedure Operation; virtual; abstract;
procedure Add(AComponent: TComponent); virtual;
procedure Remove(AComponent: TComponent); virtual;
//---
property Count: integer read GetCount;
property Childs[Index: integer]: TComponent read GetChilds;
end;
TLeaf = class(TComponent)
public
procedure Operation; override;
end;
TComposite = class(TComponent)
private
FChilds: array of TComponent;
protected
function GetChilds(Index: integer): TComponent; override;
function GetCount: integer; override;
public
constructor Create;
destructor Destroy; override;
//---
procedure Operation; override;
procedure Add(AComponent: TComponent); override;
procedure Remove(AComponent: TComponent); override;
end;
implementation
procedure TComponent.Add(AComponent: TComponent);
begin
raise Exception.Create('不支持该方法');
end;
procedure TComponent.Remove(AComponent: TComponent);
begin
raise Exception.Create('不支持该方法');
end;
function TComponent.GetChilds(Index: integer): TComponent;
begin
Result := nil;
end;
function TComponent.GetCount: integer;
begin
Result := 0;
end;
procedure TLeaf.Operation;
begin
ShowMessage('Leaf');
end;
constructor TComposite.Create;
begin
SetLength(FChilds,0);
end;
destructor TComposite.Destroy;
//---
procedure _Clear;
var
i: integer;
begin
for i := low(FChilds) to high(FChilds) do
FChilds[i].Free;
SetLength(FChilds,0);
end;
begin
_Clear;
//---
inherited;
end;
procedure TComposite.Add(AComponent: TComponent);
var
ACount: integer;
begin
ACount := Length(FChilds);
SetLength(FChilds,ACount + 1);
FChilds[ACount] := AComponent;
end;
procedure TComposite.Remove(AComponent: TComponent);
//---
function _IndexOf: integer;
var
i: integer;
begin
for i := low(FChilds) to high(FChilds) do
begin
if FChilds[i] = AComponent then
begin
Result := i;
exit;
end;
end;
//---
Result := -1;
end;
//---
procedure _Delete(AIndex: integer);
var
ACount: integer;
begin
ACount := Length(FChilds);
if AIndex < ACount - 1 then
Move(FChilds[AIndex + 1],FChilds[AIndex],(ACount - AIndex - 1) * 4);
SetLength(FChilds,ACount - 1);
end;
var
AIndex: integer;
begin
AIndex := _IndexOf;
if AIndex >= 0 then
_Delete(AIndex);
end;
function TComposite.GetChilds(Index: integer): TComponent;
begin
Result := FChilds[Index];
end;
function TComposite.GetCount: integer;
begin
Result := Length(FChilds);
end;
procedure TComposite.Operation;
var
i: integer;
begin
ShowMessage('Composite');
//---
for i := low(FChilds) to high(FChilds) do
FChilds[i].Operation;
end;
end.
procedure TForm1.Button1Click(Sender: TObject);
//---
function _GetComposite: TComponent;
begin
Result := TComposite.Create;
end;
//---
function _GetComposite1: TComponent;
begin
Result := TLeaf.Create;
end;
var
AComponent: TComponent;
begin
AComponent := _GetComposite1;
try
AComponent.Add(TLeaf.Create);
AComponent.Operation;
finally
AComponent.Free;
end;
end;