AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil
Thema durchsuchen
Ansicht
Themen-Optionen

Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

Ein Thema von mytbo · begonnen am 23. Jun 2023 · letzter Beitrag vom 26. Jun 2023
Antwort Antwort
mytbo

Registriert seit: 8. Jan 2007
461 Beiträge
 
#1

Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 23. Jun 2023, 23:56
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:
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;
Angewendet wie folgt:
Delphi-Quellcode:
var person: TPerson := dmDaten.GetPerson(idPerson);
if person <> Nil
  name := person.Caption
else
  name := 'John Doe';
2) In der nächsten Überarbeitung erweitern wir das Datenmodul:
Delphi-Quellcode:
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;
Jetzt ist es deutlich komfortabler in der Anwendung: name := dmDaten.GetPersonCaption(idPerson); 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.

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:
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;
Der Zugriff wie folgt: name := dmDaten.GetPerson(idPerson, True).Caption; 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.

Nachtrag: Die Bezeichnung Fake-Objekt scheint zu unpräzise. Dahinter kann sich auch ein Default-Objekt verbergen.
Delphi-Quellcode:
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;
Ich habe das Beispiel angepasst.

Bis bald...
Thomas
Angehängte Dateien
Dateityp: zip TestFakeObjektSource.zip (2,8 KB, 0x aufgerufen)

Geändert von mytbo (24. Jun 2023 um 16:30 Uhr) Grund: Beispiel Quelltext überarbeitet
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.685 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 24. Jun 2023, 10:14
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;
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
peterbelow

Registriert seit: 12. Jan 2019
Ort: Hessen
672 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 24. Jun 2023, 13:02
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.

Bis bald...
Thomas
Das nennt sich "NULL object pattern" und hat durchaus seinen Nutzen. Wenn konsequent verwendet spart es an vielen Stellen Test von Rückgabewerten auf nil und dergleichen. Allerdings versteckt man dadurch auch Fehlerfälle, die dem Benutzer besser zur Kenntnis gebracht werden sollten...
Peter Below
  Mit Zitat antworten Zitat
mytbo

Registriert seit: 8. Jan 2007
461 Beiträge
 
#4

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 24. Jun 2023, 16:40
Das nennt sich "NULL object pattern" und hat durchaus seinen Nutzen. Wenn konsequent verwendet spart es an vielen Stellen Test von Rückgabewerten auf nil und dergleichen. Allerdings versteckt man dadurch auch Fehlerfälle, die dem Benutzer besser zur Kenntnis gebracht werden sollten...
Danke für den Hinweis. Vielleicht ist es nur Semantik, aber ich hatte das Entwurfsmuster Nullobjekt etwas anders verstanden:
Zitat:
Instead of using a null reference to convey absence of an object (for instance, a non-existent customer), one uses an object which implements the expected interface, but whose method body is empty. The advantage of this approach over a working default implementation is that a null object is very predictable and has no side effects: it does nothing.
Mein Verständnis der Umsetzung ist etwas allgemeiner. Es kann eben mehr als nichts sein, z.B. ein Default-Objekt. Die Verwendung des Wortes "Fake-Objekt" sollte dem Rechnung tragen. Vielleicht lege ich die Definition das Pattern auch nur zu eng aus. Begebe mich auf Recherche.

Bis bald...
Thomas
  Mit Zitat antworten Zitat
mytbo

Registriert seit: 8. Jan 2007
461 Beiträge
 
#5

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 24. Jun 2023, 16:52
Nur mal auf die schnelle zusammengebasteln wie ich es meist klassisch mache ...
Danke für deinen Beitrag. Da du mORMot verwendest, kannst du dein Datenobjekt auch von der Klasse TObjectWithID ableiten und/oder dein dynamisches Array mit TDynArray verwalten. Dadurch bekommst du viel Funktionalität geschenkt.

Bis bald...
Thomas
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 24. Jun 2023, 18:57
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.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.351 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 24. Jun 2023, 18:59
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.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
freimatz

Registriert seit: 20. Mai 2010
1.384 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 26. Jun 2023, 08:51
Stimme dem zu außer:
Denn auf das Fakeobjekt kann man ja zugreifen, ohne dass ein Fehler passiert
Man könnte ja es so machen, dass das "Fakeobjekt" den Fehler erzeugt z.B. eine Exceptionen.
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.

Wir machen Nullobjekte in der Regel (nur) dann, wenn das Nichtvorhandensein ein Anwendungsfall ist.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.351 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Die Arbeit mit Fake-Objekten erspart die Prüfung auf Nil

  Alt 26. Jun 2023, 09:46
Man könnte ja es so machen, dass das "Fakeobjekt" den Fehler erzeugt z.B. eine Exceptionen.
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.
Das ist sicherlich richtig, aber dafür lohnt sich meiner Meinung der Aufwand nicht, da das ja nicht oft passieren sollte. Der Vorteil eines Fakeobjekts wäre dann ja weg.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Alt 21. Aug 2023, 05:46     Erstellt von redball4az
Dieser Beitrag wurde von TBx gelöscht. - Grund: Verdacht auf SPAM und den damit verbundenen verschwenderischen Umgang von wertvollen Bits und Bytes
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:33 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz