Design Patterns: Observer with Aspect
IAspect = interface
[GUID]
function CompareTo(const Other: IAspect): Boolean;
function GetName: string;
end;
TAspect = class
private
fName: string;
protected
constructor Create(const Name: string); overload;
// IAspect
function CompareTo(const Other: IAspect): Boolean;
function GetName: string;
end;
implementation
constructor TAspect.Create(const Name: string);
begin
inherited Create;
fName := Name;
end;
function TAspect.CompareTo(const Other: IAspect): Boolean;
begin
Result := Other = self as IAspect;
if not Result then
Result := Other.GetName = fName;
end;
function TAspect.GetName: string;
begin
Result := fName;
end;
//////////////////////////////////////////
TListAspectEnum = (listAppend, listInsert, listDelete, listChanged,
listEmpty);
IListAspect = interface(IAspect)
[GUID]
function EnumValue: TListAspectEnum;
function GetIdx: Integer;
function GetItem: TObject;
procedure SetIdx(Value: Integer);
procedure SetItem(const Value: TObject);
end;
//////////////////////////
TListAspect = class(TAspect, IListAspect)
public
class function Append: IListAspect;
class function Changed: IListAspect;
class function Delete: IListAspect;
class function Empty: IListAspect;
class function Insert: IListAspect;
private
fEnumValue: TListAspectEnum;
fIdx: Integer;
fItem: TObject;
constructor Create(EnumValue: TListAspectEnum);
protected
// IListAspect
function EnumValue: TListAspectEnum;
function GetIdx: Integer;
function GetItem: TObject;
procedure SetIdx(Value: Integer);
procedure SetItem(const Value: TObject);
end;
implementation
constructor TListAspect.Create(EnumValue: TListAspectEnum);
begin
inherited Create(GetEnumName(TypeInfo(TListAspectEnum), Ord(EnumValue)));
fEnumValue := EnumValue;
end;
class function TListAspect.Append: IListAspect;
begin
Result := Create(listAppend);
end;
class function TListAspect.Changed: IListAspect;
begin
Result := Create(listChanged);
end;
class function TListAspect.Delete: IListAspect;
begin
Result := Create(listDelete);
end;
class function TListAspect.Empty: IListAspect;
begin
Result := Create(listEmpty);
end;
class function TListAspect.Insert: IListAspect;
begin
Result := Create(listInsert);
end;
function TListAspect.EnumValue: TListAspectEnum;
begin
Result := fEnumValue;
end;
function TListAspect.GetIdx: Integer;
begin
Result := fIdx;
end;
function TListAspect.GetItem: IInterface;
begin
Result := fItem;
end;
procedure TListAspect.SetIdx(Value: Integer);
begin
fIdx := Value;
end;
procedure TListAspect.SetItem(const Value: TObject);
begin
fItem := Value;
end;
/////////////////////////////////////
This can then be used in the Aspect Observer; here are the definitions for
the extended classes
AspectObserver
procedure Update(const Subject: TObject; const Aspect: IAspect);
AspectSubject
procedure Attach(const Observer: AspectObserver);
procedure Detach(const Observer: AspectObserver);
procedure Notify(const Aspect: IAspect);
When using the ListAspect inside a List class, the syntax you get is
something like this :
procedure TListClass.InsertItem(Idx: Integer; const Item: TObject);
var
aspect: IListAspect;
begin
...
aspect := TListAspect.Insert;
aspect.SetIdx(Idx);
aspect.SetItem(Item);
fSubject.Notify(aspect);
...
end;
And in an Observer you would do something like this :
var
ListAspect: IListAspect
InsertedAt: Integer;
Item: TObject;
begin
if Supports(Aspect, IListAspect, ListAspect) then
begin
if ListAspect.CompareTo(TListAspect.Insert) then
begin
InsertedAt := ListAspect.GetIdx;
Item := ListAspect.GetItem;
...
end;
...
...
Things are a lot easier in .NET as enums can be declared internally to a
class and stored in a System.Enum field. You don't need to use interfaces as
you get true garbage collection of temporary objects. You can truly hide the
default TObject constructor so that folks cannot instantiate an Aspect apart
form using the named class methods.