Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Allgemeines zu Klassen / Objekten (https://www.delphipraxis.net/3157-allgemeines-zu-klassen-objekten.html)

Luckie 26. Feb 2003 13:34


Allgemeines zu Klassen / Objekten
 
Nehmen wir mal folgende Klassen oder sind es Objekte als Diskussionsgrundlage:

Klasse / Objekt 1:
Delphi-Quellcode:
type TFooClass = class
  private
    FFooCaption : String;
    procedure SetFooCaption;
    funktion GetFooCaption: String;
    procedure FooCaptionPlusSr;
  public
    constructor create;
end;

procedure TFooClass.create;
begin
  SetFooCaption;
end;

procedure TFooClass.SetFooCaption;
begin
  FFooCaption := 'Hello';
end;

function TFooCaption.GetFooCaption: String;
begin
  result := FFooCaption;
end;

procedure TFooClass.FooCaptionPlusStr;
begin
  FFoocaption := FFooCaption+', world';
end;
Variante 2:
Delphi-Quellcode:
type TFooClass = Class
  private
    FFooCaption: String;
    procedure FooCaptionPlusSr;
  public
    constructor create;
end;

constructor TFooClass.Create;
begin
  FFooCaption := 'Hello';
end;

procedure TFooClass.FooCaptionPlusStr;
begin
  FFoocaption := FFooCaption+', world';
end;
So, der Unterschied sollte klar sein. Mit Variante eins kann ich über Methoden auf die Membervariable FFooCaption zugreifen. Aber man kann ja auch so drauf zugreifen:
Delphi-Quellcode:
var
  FooClass: TFooClass;
  s : String;
begin
  FooClass := TFooClass.Create;
  ...;
  ...;
  s := FooClass.FFooCaption;
Jetzt stellt sich mir die Frage wie macht man es richtig oder sollte man es machen? Immer über Methoden auf die Membervariablen zu greifen oder geht es auch direkt, wie in Beispiel 2? Bitte mit Begründung antworten, sonst nützt es mir nicht viel.

Noch was. Wann müssen / sollen Methoden, Membervariablen unter public deklariert werden?

sakura 26. Feb 2003 13:46

Die Antworten, welche Du jetzt erwartest basieren wohl größtenteils auf "Glauben". Kurz, jeder wie es ihm gefällt.

Deshalb einfach mal, wie ich es mache und mit der Lösung fahre ich recht gut und wohl auch entlang (oder mitten im) Mainstream.
Delphi-Quellcode:
type TFooClass = class
  private
    FFooCaption : string;
    procedure SetFooCaption(const Value: string);
    function GetFooCaptionEx: string;
  public
    constructor create;
    property FooCaption: string read FFooCaption write SetFooCaption;
    // kleiner fehler korriegert (Ex am Ende vergessen)
    property FooCaptionEx: string read GetFooCaptionEx;
  end;
Im Create setze ich i.A. die Variable (ioB: FFooCaption) direkt, sofern der Wert konstant ist bzw. nicht validiert werden muss. Wird mit dem Setzen gleichzeitig eine Validierung durchgeführt, gehe ich über die Property und damit ultimativ auch über die write Methode. Allerdings versuche ich im Create eine solche Situation zu vermeiden und zwinge den Programmiere die Werte anschließend separat zu setzen, damit werden auch hier entstehende Fehlerquellen offensichtlicher.

Zitat:

Noch was. Wann müssen / sollen Methoden, Membervariablen unter public deklariert werden?
Würde ich i.A. als Quatsch ansehen, wenn man es so verallgemeinert. Variablen sind bei mir generell im private Bereich zu finden. Wenn von ausserhalb Zugriff gewährt wird, dann ausschließlich über Properties und Methoden. Methoden findest Du bei mir je nach Bedarf in den Bereichen private, protected und public. Damit legt man anschliessend ja auch fest, wer und wie auf die Methoden zugegriffen wird. Properties sind bei mir allgemein in den Bereichen protected, public und published zu finden, selten im private Bereich, aber auch das kann passieren, ein, zwei Mal im Leben. :D

...:cat:...

Luckie 26. Feb 2003 13:59

Jetzt hast du noch eine Möglichkeit eingebracht mit property. :? Was ist denn der Vorteil, wenn man über property geht und es nicht so macht wie ich im ersten Beispiel?

Also wenn der Wert überprüft werden muß, würdest du über eine Methode gehen? Klingt sinnvoll anderst ist es ja auch nicht möglich, es sei denn man sagt der Programmierer ist selber dafür verantwortlich. Aber das wäre ja blöd dann muß er ja, wenn er mit dem Objekt arbeitet immer selber eine Überprüfung machen. ist also sinnvoll dies von der Klasse erledigen zu lassen. OK, das habe ich verstanden.

Aber um noch mal auf private und public zu sprechen zu kommen: Warum steht das
Delphi-Quellcode:
property FooCaption: string read FFooCaption write SetFooCaption;
property FooCaptionEx: string read GetFooCaption;
nicht im private-Abschnitt bei dir?

Nachtrag: Ich kann doch auch den Wert von FooCaption bekommen (bei dir) mit:
Delphi-Quellcode:
  s := FooCaption.GetFooCaptionEx;

sakura 26. Feb 2003 14:29

Erst mal schnell: im obigen Beitrag habe ich im Code noch einen kleinen Fehler korrigiert ;)

Delphi-Quellcode:
type TFooClass = class
  private
    FFooCaption : string;
    procedure SetFooCaption(const Value: string);
    function GetFooCaptionEx: string;
  public
    constructor Create;
    property FooCaption: string read FFooCaption write SetFooCaption;
    // kleiner fehler korriegert (Ex am Ende vergessen)
    property FooCaptionEx: string read GetFooCaptionEx;
  end;

constructor TFooClass.Create;
begin
  inherited;
  FFooCaption := 'Default';
end;

procedure TFooClass.SetFooCaption(const Value: string);
begin
  if FFooCaption <> Value then
  begin
    // überprüfen, ob der wert "ok" ist
    if Trim(Value) = '' then
      raise Excpetion.Create('FooCaption muss einen nicht-leeren String enthalten');
    FFooCaption := Trim(Value);
  end;
end;

function TFooClass.GetFooCaptionEx: string;
begin
  Result := Format('Die FooCatpion ist "%s".', [FooCaption]);
end;
So würde das erst einmal bei mir aussehen. In Create greife ich direkt auf die Variable FFooCaption zu, da der Wert eh konstant ist und somit keiner Überrpüfung bedarf. Wenn ich die Regeln ändere muss ich allerdings manuell überprüfen, ob der konstante Startwert immer noch den Bedingungen entspricht ;)

Wenn die Eigenschaft FooCaption gesetzt wird, dann überprüft die Funktion jetzt, ob der neue Wert auch alle Regeln entspricht. Hier könnte auch noch andere Routinen (z.B. Neu Zeichen :: TControl.Invalidate) und Ereignishandler (TOnEmptyValue) vorkommen.

In der Funktion GetFooCaptionEx greife ich dann auf FooCaption und nicht auf FFooCaption zurück. In der aktuellen Lösung ist das zwar genau das Gleiche, falls sich die Implementation jedoch mal ändern sollte, so das vor der Rückgabe von FooCaption noch eine Methode ausgeführt wird, dann stimmt es auch nach der Änderung der Implementation sofort.

Property vs. Funktionen
Da gibt es nicht viel zu sagen, ausser dass es wahrscheinlich schneller zu tippen geht, wenn ich immer nur Object.FooCaption für setzen/auslesen schreiben muss, anstatt von Object.GetFooCaption für das Lesen und Object.SetFooCaption(...) für das Schreiben. Es ist einfach "schöner". Ausserdem macht es anderen Entwicklern den Umgang mit Deinen Objekten leichter.

Noch kurz zu der Frage, wie Du die FooCaptionEx erhälst:
Delphi-Quellcode:
var
  FooClass: TFooClass;
begin
  FooClass := TFooClass.Create;
  try
    Self.Caption := FooClass.FooCaptionEx; // Self ist z.B. TForm1
  finally
    FooClass.Free;
  end;
Folgender Code:
Delphi-Quellcode:
property FooCaption: string read FFooCaption write SetFooCaption;
property FooCaptionEx: string read GetFooCaption;
steht im public Bereich, damit vorhergendes Konstrukt auch dann funktioniert, wenn TFooClass und TForm1 in verschiedenen Units deklariert sind.

...:cat:...

Luckie 26. Feb 2003 15:07

Zitat:

Zitat von sakura
Property vs. Funktionen
wenn ich immer nur Object.FooCaption für setzen/auslesen schreiben muss, anstatt von Object.GetFooCaption für das Lesen und Object.SetFooCaption(...) für das Schreiben. Es ist einfach "schöner". Ausserdem macht es anderen Entwicklern den Umgang mit Deinen Objekten leichter.

Gut. Auch kapiert.
Zitat:

Folgender Code:
Delphi-Quellcode:
property FooCaption: string read FFooCaption write SetFooCaption;
property FooCaptionEx: string read GetFooCaption;
steht im public Bereich, damit vorhergendes Konstrukt auch dann funktioniert, wenn TFooClass und TForm1 in verschiedenen Units deklariert sind.
Auch verstanden.

Danke für deine Erklärungen. Mal sehen, wie ich das in Zukuft umsetzen kann.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:28 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