Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi kleines OOP Beispiel bitte um Anmerk./Verbesserungvorschläge (https://www.delphipraxis.net/122342-kleines-oop-beispiel-bitte-um-anmerk-verbesserungvorschlaege.html)

sirius 14. Okt 2008 18:58

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
Dann lass doch den Setter bei Ergebnis weg und lass auch gleich fErgebnis weg und gebe bei GetErgebnis einfach die summe zurück.

DeddyH 14. Okt 2008 18:58

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
Du darfst dann auch nicht die Setter-Methoden direkt aufrufen, sondern müsstest die Properties setzen, welche dann wiederum in ihrem jeweiligen Setter die Berechnung durchführen können. Das kann auch durchaus sinnvoll sein, ich sehe da jetzt keinen Widerspruch zur OOP.

[edit]
Zusammengefasst könnte das dann so aussehen:
Delphi-Quellcode:
  TMeineDaten = class(TObject)
    private
      fWert1 : Integer;
      fWert2 : Integer;
      procedure SetWert1(const Value: Integer);
      procedure SetWert2(const Value: Integer);
      function GetErgebniss(): Integer;
    public
      property Wert1 : Integer read FWert1 write SetWert1 Default 0;
      property Wert2 : Integer read FWert2 write SetWert2 Default 0;
      property Ergebnis : Integer read GetErgebnis;
  end;

procedure TMeineDaten.SetWert1(const Value: Integer);
begin
  FWert1 := Value;  
end;

procedure TMeineDaten.SetWert2(const Value: Integer);
begin
  FWert2 := Value;
end;

function TMeineDaten.GetErgebniss(): Integer;
begin
  Result := fWert1 + fWert2;
end;
[/edit]

jfheins 14. Okt 2008 19:55

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
Ich würde allerdings ie Getter uns Setter protected machen, um jemendem, der die Klasse ableiten möchte, das Leben nicht unnötig kompliziert zu machen ;)

Ist aber nur eine klitzekleine Anregung, ich will ja keinen überfordern :stupid:

newbe 14. Okt 2008 20:25

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
@DeddyH

bin begeistert von deinem Code. :)

Also das sieht doch echt schlank und elegant aus. Ich würde nur gern das Ergebnis im Object storen, da er ja sonst bei jedem
zugriff auf die Property diese Berechnung ausführt. Und das kann bei Komplexen sachen schon mächtig performance kosten.

Also sprich die Funktion GetErgebniss soll intern Checken ob die Berechnung schon ausgeführt wurde, und das Ergebnis dann
aus der Property fErgebnis holen, anstatt bei jedem zugriff nochmal zu berechnen.

Aber ansonsten finde ich deinen Code top. :Thumbs up:

mfG newbe

sirius 14. Okt 2008 20:33

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
Delphi-Quellcode:
TMeineDaten = class(TObject)
    private
      fWert1 : Integer;
      fWert2 : Integer;
      FErgebnis: Integer;
      FBerechnet: Boolean;
      procedure SetWert1(const Value: Integer);
      procedure SetWert2(const Value: Integer);
      function GetErgebniss(): Integer;
    public
      property Wert1 : Integer read FWert1 write SetWert1 Default 0;
      property Wert2 : Integer read FWert2 write SetWert2 Default 0;
      property Ergebnis : Integer read GetErgebnis;
  end;

procedure TMeineDaten.SetWert1(const Value: Integer);
begin
  FWert1 := Value;  
  FBerechnet:=false;
end;

procedure TMeineDaten.SetWert2(const Value: Integer);
begin
  FWert2 := Value;
  FBerechnet:=False;
end;

function TMeineDaten.GetErgebniss(): Integer;
begin
  if not FBerechnet then
  begin
    FErgebnis:= fWert1 + fWert2;
    FBerechnet:=true;
  end;
  result:= FErgebnis;
end;
Ist mir zu lang, dass jetzt zu erklären.
FBerechnet könnte man noch im constrcutor initialisieren.

newbe 15. Okt 2008 04:52

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
@sirius

Genau so habe ich mir das vorgestellt. :)

Eine letzte Frage hätte ich noch zu dem Themenkomplex. Unzwar zum constructor. Du weist darauf hin, das es wichtig wäre
den Boolean zu initialisieren, was ja auch richtig ist. Jedoch verstehe ich eines nicht. Im Inet habe ich irgendwo aufgeschnappt,
das man Klassenvariablen bzw. Felder immer erst nach abgeschlossener Object-Erstellung initialisieren sollte. Ich habe das so verstanden, das ich erst das Object erstellen soll und erst dann eine extra Init Prozedure aufrufen soll um Seiteneffekte zu vermeiden. Also in dieser Art.

Delphi-Quellcode:

procedure bla.init;
begin

Wert1 : Integer = 0; //Initialisieren
Wert2 : Integer = 0;
Ergebnis: Integer = 0;
FBerechnet: Boolean = false;

end;

....

TForm1.buttonclick(Sender: TObject);
var bla: TMeineDaten;

begin

bla:=TMeineDaten.create;
bla.init;

end;
Stimmt das den jetzt, oder ist da was wahres dran? Kann ich jede Art von Klassenvariable im Constructor initialisieren oder
kann es da probleme geben weil die Objecterstellung ja im Constructor noch nicht abgeschlossen ist?

mfG newbe

sirius 15. Okt 2008 07:56

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
Die Objekterstellung ist abgeschlossen, wenn du im constrcutor bist.
Der Code zum Erstellen der Instanz fügt der Compiler bei dem Wort "begin" des Constructors ein. Also, wie gesagt, direkt am Anfang.

Edit: Vielleicht mal konkret.
Jede Methode bekommt ja immer mindestens den Parameter "self" übergeben. Damit kannst du ja auf die Felder deiner Klasse zugreifen. Der Constructor bekommt zusätzlich noch einen Wert mitgegeben. Dieser sagt aus, ob der Constructor eine neue Instanz erstellen soll.

Hier mal eine normale Methode
Delphi-Quellcode:
//aus folgendem Code:
procedure TMeineDaten.SetWert1(const Value: Integer);
begin
  FWert1 := Value;  
  FBerechnet:=false;
end;

//wird eigentlich:
procedure TMeineDaten.SetWert1(Self:Pointer; const Value: Integer);
begin
  Self.FWert1 := Value;  
  Self.FBerechnet:=false;
end;
Und self zeigt einfach auf eine Art Record, in dem alle deiner Felder (Klassenvariablen) enthalten sind. Self ist dann auch das, was in deiner Objektinstanz "MeineDaten" (oder in Post #10: "test") steht. Ist halt einfach nur ein Zeiger auf deinen InstanzRecord. Und jede Instanz hat ihren eigenen Record.

So, und jetzt noch mal zum Constrcutor. Der hat, wie gesagt, neben "self" noch einen versteckten Parameter:
Delphi-Quellcode:
//aus folgendem Code:
Constructor TMeineDaten.Create;
begin
  fBerechnet:=false;
end;

//wird dann:
Constructor TMeineDaten.Create(Self:Pointer; doCreateClass:Boolean);
begin
  if doCreateClass then
  begin
    Self:=_ClassCreate(Self, True); //Ab hier existiert dein Objekt.
    Result:=Self;
  end;

  Self.fBerechnet:=false;


  if DoCreateClass then
    _AfterConstruction(Self);
end;
Was macht eigentlich _ClassCreate (neben Exceptionbehandlung und pipapo). Es reserviert einfach Speicher mittels new, wie bei einem normalen Record, den man dynamisch anlegt auch. Und dann wird die Adresse auf den Speicher zurückgegeben.

So, und jetzt gibt es zwei Aufrufmöglichkeiten des Constructors:
Delphi-Quellcode:
test:=TMeindeDaten.create;
Hier wird DoCreateClass auf True gesetzt und anstatt Self wird ein Zeiger auf die TypInformationen von TMeineDaten übergeben (also TMeineDatenClass oder einfach TClass)
Wenn du jetzt den Constructor (irendwann nach der Instanzierung) nochmal so aufrufst (quasi, wie jede andere Methode auch):
Delphi-Quellcode:
test.create;
dann ist DoCreateClass=False und an self wird eben der Wert von test übergeben (wie bei jeder anderen Methode auch).
Nebenbei: Wenn jetzt test noch nicht instanziert wäre und du einfach test.create aufrufst (Was ein typischer Delphi-Anfängerfehler ist), wird für self eben nil (oder ein anderer krummer Wert) übergeben und DoCreateClass ist natürlich false. Dann greifst du hier auf den Record nil.fBerechnet zu, was eine Access Violation, meist in der Nähe von Addresse 0 = nil, auslöst.

Allerdings ist es unüblich test.Create direkt aufzurufen. Der Zustand tritt eher ein, wenn man von TMeineDaten eine neue Klasse ableitet und dort, in einem neuen Constructor, dann inherited aufruft. Dann wird der Vorfahr, was TMeineDaten.Create ist, mit DoClassCreate=False aufgerufen.

Ich hoffe ich habe nicht zu sehr verwirrt. Hier ist es auch nochmal schön beschrieben.

Edit2:
Erst die Klasse instanzieren und dann einen weiteren Befehl zur Initialisierung aufrufen ist in anderen Sprachen (die wir hier nicht erwähnen wollen) notwendig. In Delphi kannst du instanzieren und initialisieren in einem Abwasch, eben im constructor, machen.

newbe 15. Okt 2008 10:06

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
@sirius

Vielen Dank an Dich für deine ausführliche Erklärung dazu. :)

herzliche Grüße

newbe

newbe 15. Okt 2008 15:28

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
@All

Ich habe es jetzt so gemacht.

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;


TMeineDaten = class(TObject)
    private
      fWert1 : Integer;
      fWert2 : Integer;
      FErgebnis: Integer;
      FBerechnet: Boolean;
      procedure SetWert1(const Value: Integer);
      procedure SetWert2(const Value: Integer);
      function GetErgebniss: Integer;
    public
      constructor create;
      property Wert1 : Integer read FWert1 write SetWert1;
      property Wert2 : Integer read FWert2 write SetWert2;
      property Ergebnis : Integer read GetErgebniss;
  end;


var
  Form1: TForm1;

implementation

{$R *.dfm}



{ TMeineDaten }

procedure TMeineDaten.SetWert1(const Value: Integer);
begin

  if Value=5 then raise Exception.create('Wert 5 wird nicht als Eingabe akzeptiert!');

  FWert1 := Value;
  FBerechnet := false;
end;

procedure TMeineDaten.SetWert2(const Value: Integer);
begin

  if Value=5 then raise Exception.create('Wert 5 wird nicht als Eingabe akzeptiert!');

  FWert2 := Value;
  FBerechnet := false;
end;

constructor TMeineDaten.create;
begin
  FBerechnet := false;
end;

function TMeineDaten.GetErgebniss: Integer;
begin
  if not FBerechnet then
  begin
    FErgebnis := fWert1 + fWert2;
    FBerechnet := true;
  end;
  result := FErgebnis;
end;

procedure TForm1.Button1Click(Sender: TObject);
var test: TMeineDaten;
begin
  test:=TMeineDaten.create;
  test.Wert1:=strtoint(edit1.text);
  test.Wert2:=strtoint(edit2.text);
  label1.caption:=inttostr(test.ergebnis);
  test.Free;
end;

end.
Erstmal die Frage ob das so OOP Konform durchgeht? Und dann hätte ich da noch eine Frage zur Eingabe-Exeptionbehandlung. Was wäre OOP Konform günstiger,
die Exeptionbehandlung in die Klasse zu integrieren (im Setter) oder wie hier gehabt in einer Controlprocedure der VCL?

Ich hätte die Exeptionbehandlung numal gerne komplett in der Klasse. So wie ich es jetzt mache, kann ich jedoch im Setter nicht prüfen, ob ein leeres
Editfeld vorliegt. Delphi schmeist zwar eine Econvert Error Exeption wegen dem strtoint, jedoch hätte ich gerne eigene Exeptions und dann alle
Bedingungsabfragen in der Klasse. Dazu müsste ich ja dem Setter ein String übergeben, die strtoint Konvertierung ausführen und dann aber einen Integer schreiben. Wäre solch eine vorgehensweise überhaupt Sinnvoll? Wie könntedafür die property Deklaration aussehen? Bei allem was ich probiert habe,
beschwert sich Delphi wegen inkompatibler Typen.

mfG newbe

sirius 15. Okt 2008 17:05

Re: kleines OOP Beispiel bitte um Anmerk./Verbesserungvorsch
 
Wichtig für dich wäre noch try..finally. Das benutzt man wie folgt:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var test: TMeineDaten;
begin
  test:=TMeineDaten.create;
  try
    test.Wert1:=strtoint(edit1.text);
    test.Wert2:=strtoint(edit2.text);
    label1.caption:=inttostr(test.ergebnis);
  finally
    test.Free; //Der Teil wird immer ausgeführt, egal ob Exception kommt oder nicht
  end;
end;
Ansonsten würdest du eine Exception werfen, das PRogramm springt raus aus deinen Methoden und du rufst nie Free auf -> Speicherloch

Exceptions solltest du immer in den unteren Klassen werfen (so wie du es in TMeineDaten gemacht hast) und in der Klasse, welche sowieso für Anzeige (GUI) zuständig ist anzeigen. Die VCL macht das ja schonmal, in dem sie spätestens am Ende der Ereignis-/Messagebehandlung einen Except-Block hat und dann mittels Application.Showexception ein Fenster bringt. Wenn du also am Exceptiontest nichts verändern willst, dann kannst du es so durchlaufen lassen ohne eigene Exceptionbehandlung (try..except).
Ansonsten kann man an dem Beispiel jetzt nicht viel mehr sagen. Ausser dass du in dem speziellen Fall anstatt inttostr auch tryintsotr nehmen kannst um Fehler gleich abzufangen bzw. eine eigene Exception zu kreieren.


Eine übliche Vorgehensweise ist auch in bestimmten Klassen die Exception abzufangen und dann gleich wieder zu werfen, aber mit einer eigenen Ergänzung:
Delphi-Quellcode:
try
 ...
except
  on e:Exception do
    raise Exception.create(e.MEssage+#13#10+'Ich will auch noch was sagen!');
end;
Und nicht zu vergessen auch mal eigene Exceptionklassen zu verwenden.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:34 Uhr.
Seite 2 von 3     12 3      

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