![]() |
Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Liste der Anhänge anzeigen (Anzahl: 1)
Zugegeben, die Überschrift ist etwas zugespitzt formuliert, es soll zur Diskussion anregen. Starten wir mit Bekanntem. Gegeben ist die Situation: Arbeit mit einem Geschäftsobjekt (Business Object).
1) Die Definition sieht wie folgt aus:
Delphi-Quellcode:
Angewendet wie folgt:
type
TPerson = class(TObject) private FID: TID; FVorname: String; FNachname: String; function GetCaption: String; public constructor Create(pmID: TID); reintroduce; property Caption: String // Format: Nachname, Vorname read GetCaption; property ID: TID read FID; property Vorname: String read FVorname write FVorname; property Nachname: String read FNachname write FNachname; end; type TdmDaten = class(TDataModule) public function GetPerson(pmPersonID: TID): TPerson; end; function TdmDaten.GetPerson(pmPersonID: TID): TPerson; begin if IsValidID(pmPersonID) then Result := FPersonList.Find(pmPersonID) else Result := Nil; end;
Delphi-Quellcode:
2) In der nächsten Überarbeitung erweitern wir das Datenmodul:
var person: TPerson := dmDaten.GetPerson(idPerson);
if person <> Nil name := person.Caption else name := 'John Doe';
Delphi-Quellcode:
Jetzt ist es deutlich komfortabler in der Anwendung:
type
TdmDaten = class(TDataModule) public ... function GetPersonCaption(pmPersonID: TID): String; end; function TdmDaten.GetPersonCaption(pmPersonID: TID): String; begin var person: TPerson := GetPerson(pmID); if person <> Nil then Result := person.Caption else Result := 'John Doe'; end;
Delphi-Quellcode:
In der realen Welt kann ein Geschäftsobjekt viele Eigenschaften haben. Nicht alle kann/will man über einen Direktzugriff zugänglich machen. Damit wären wir wieder am Anfang und beim ersten Beispiel.
name := dmDaten.GetPersonCaption(idPerson);
3) Was wäre, wenn es immer ein Objekt gibt. Wenn kein reales vorhanden ist, dann auf Wunsch ein Fake-Objekt. Es könnte wie folgt aussehen:
Delphi-Quellcode:
Der Zugriff wie folgt:
type
TPerson = class(TObject) public function IsFake: Boolean; ... end; TdmDaten = class(TDataModule) public function GetPerson(pmPersonID: TID; pmFakeReturn: Boolean = False): TPerson; ... end; function TPerson.IsFake: Boolean; begin Result := (Self = __FakePerson); end; function TdmDaten.GetPerson(pmPersonID: TID; pmFakeReturn: Boolean): TPerson; begin Result := Nil; if IsValidID(pmPersonID) then Result := FPersonList.Find(pmPersonID); if (Result = Nil) and pmFakeReturn then Result := __FakePerson; end; initialization __FakePerson := TPerson.Create(0); __FakePerson.Vorname := 'John'; __FakePerson.Nachname := 'Doe'; finalization __FakePerson.Free;
Delphi-Quellcode:
Diese Schreibweise sieht elegant aus, beinhaltet aber auch Gefahren. Allerdings nichts, was bei disziplinierter Programmierung ein Problem sein sollte. Zur Zeit arbeite ich mit Fall 1), 2) und anderen Techniken. Gebe aber zu, der Ansatz mit dem Fake-Objekt hat einen gewissen Charme und schwirrt im Kopf herum. Im Anhang der Sourcecode zum Spielen.
name := dmDaten.GetPerson(idPerson, True).Caption;
Nachtrag: Die Bezeichnung Fake-Objekt scheint zu unpräzise. Dahinter kann sich auch ein Default-Objekt verbergen.
Delphi-Quellcode:
Ich habe das Beispiel angepasst.
type
TDefaultPerson = class(TPerson) protected function GetCaption: String; override; end; function TDefaultPerson.GetCaption: String; begin if DayOfWeek(Date) = 2 then // I don't like monday Result := Format('%s, %s', ['Lecter', 'Hannibal']) else Result := inherited GetCaption; end; Bis bald... Thomas |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Nur mal auf die schnelle zusammengebasteln wie ich es meist klassisch mache ...
Code:
type
TPerson = packed record FirstName, SurName: string; Age: Integer; end; TPersons = array of TPerson; type { TPersonalClass } TPersonalClass = class(TObject) strict private FPersons: TPersons; FCount: Integer; FIndex: Integer; private procedure SetIndex(const AIndex: Integer); public constructor Create; destructor Destroy; Override; procedure Add(const AFirstName, ASurName: string; const AAge: Integer); overload; procedure Add(const APerson: TPerson); overload; function GetPerson: TPerson; procedure SetPerson(AValue: TPerson); published property Index: Integer read FIndex write SetIndex; property Count: Integer read FCount; end; { TPersonalClass } constructor TPersonalClass.Create; begin FCount := 0; FIndex := -1; SetLength(FPersons, 0); end; destructor TPersonalClass.Destroy; begin SetLength(FPersons, 0); inherited Destroy; end; procedure TPersonalClass.SetIndex(const AIndex: Integer); begin if (AIndex < FCount) then FIndex := AIndex; end; procedure TPersonalClass.Add(const AFirstName , ASurName: string; const AAge: Integer); var i: Integer; begin if ((AFirstName <> '') and (ASurName <> '')) then begin i := Length(FPersons); SetLength(FPersons, Succ(i)); FPersons[i].FirstName := AFirstName; FPersons[i].SurName := ASurName; FPersons[i].Age := AAge; FCount := Length(FPersons); if ((FIndex = -1) and (FCount > 0)) then FIndex := Pred(FCount); end; end; procedure TPersonalClass.Add(const APerson: TPerson); var i: Integer; begin if ((APerson.FirstName <> '') and (APerson.SurName <> '')) then begin i := Length(FPersons); SetLength(FPersons, Succ(i)); FPersons[i].FirstName := APerson.FirstName; FPersons[i].SurName := APerson.SurName; FPersons[i].Age := APerson.Age; FCount := Length(FPersons); if ((FIndex = -1) and (FCount > 0)) then FIndex := Pred(FCount); end; end; function TPersonalClass.GetPerson: TPerson; begin if (FIndex >= 0) then begin Result.FirstName := FPersons[FIndex].FirstName; Result.SurName := FPersons[FIndex].SurName; Result.Age := FPersons[FIndex].Age; end; end; procedure TPersonalClass.SetPerson(AValue: TPerson); begin if (FIndex >= 0) then begin FPersons[FIndex].FirstName := AValue.FirstName; FPersons[FIndex].SurName := AValue.SurName; FPersons[FIndex].Age := AValue.Age; end; end; |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Zitat:
![]() |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Zitat:
![]() Zitat:
Bis bald... Thomas |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Zitat:
Bis bald... Thomas |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
In unsererem Framework ist in der Basisklasse aller Datenobjekte die Klassenmethode NullObject vorgesehen, die eine spezielle Instance der Klasse zurückgibt.
Dabei werden einmal erzeugte NullObjekte in einem Dictionary registriert, wenn benötigt wiederverwendet und bei Programmende freigegeben. Bei einem NullObjekt werden Änderungen an Properties im Setter ignoriert. |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Das sind im Grunde zwei vollkommen unterschiedliche Aspekte. Der eine Aspekt ist, dass man statt nil immer einen neutralen Wert zurückliefert. Das geht aber nur, wenn der Anwendungsfall dafür geeignet ist, sprich dieses neutrale Objekt auch wirklich keine Auswirkungen hat. Eine Prüfung, ob das Objekt gültig ist, ist daher unnötig, was den großen Vorteil ausmacht.
Fakedaten sind eine komplett andere Schiene, bei der man statt eines neutralen Objekts ein ungültiges Fakeobjekt zurückliefert. Dann muss man aber lediglich statt der Prüfung auf nil eine andere Validitätsprüfung ausführen, was den Aufwand nicht verringert, aber fehleranfälliger ist. Denn auf das Fakeobjekt kann man ja zugreifen, ohne dass ein Fehler passiert (der auffallen würde und geloggt werden würde). Trotzdem kommt am Ende ggf. etwas Falsches heraus, was man dann ggf. aufwendig sucht. Ein Anwendungsfall wäre die Qualitätssicherung, bei der man dann die Fakedaten anzeigt, um Tests ohne Dateneingabe zu ermöglichen. Das ist aber hier ja nicht gemeint gewesen. |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Stimme dem zu außer:
Zitat:
Dann muss das nicht jeder Aufrufer machen. Bei einem Nil und einer vergessenen Abfrage würde man dann eine schönere exception bekommen und nicht eine AV. Auf der anderen Seite könnte ja dann auch schon der, der das Objekt liefern soll eine Exceoption raisen wenn es nicht geht. :cyclops: Wir machen Nullobjekte in der Regel (nur) dann, wenn das Nichtvorhandensein ein Anwendungsfall ist. |
AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:09 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz