![]() |
vererbende Klasse oder Interface? oder beides?
Hallo. Ich bin gerade am kämpfen/lernen mit Vererbung und Interfaces und bräuchte da mal eine kompetente Meinung.
Meine Problemstellung: Ich habe verschiedene Messgeräte die ich über GPIB anspreche. Das Schreiben/Lesen ist bei den Messgeräten unterschiedlich. Deshalb wollte ich ein Interface ISchreibenLesen verwenden, dessen Methoden dann jede Klasse implementieren muss. Theoretisch könnte ich ja aber auch abstrakte Methoden in der Oberklasse deklarieren und diese dann jeweils in der Unterklasse überschreiben. Kann man da sagen was besser ist? Die Oberklasse hatte ich mir gedacht, da ja alle Messgeräte gewisse gemeinsame Felder besitzen (Adresse usw.) TMessgeräte = class ((TInterfacedObject)) TMessgerätA = class (TMessgeräte, ISchreibenLesen) TMessgerätB = class (TMessgeräte, ISchreibenLesen) Und noch eine Frage. Die Messgeräte sollen eine Property Range bekommen (mit Getter und Setter). In der Oberklasse deklariert, wird die Property an die Unterklassen vererbt, die Getter/Setter müssten dann abstract sein. Oder anders, die Property im Interface deklarieren (hier auch Getter/Setter). Dann muss ich in der Unterklasse aber wieder eine Property deklarieren, das wird irgendwie doch nicht mehr so übersichtlich. Wie würdet ihr sowas machen? Wie gesagt, bin da noch stark am Anfang mit Interfaces und Vererbung und für jede Anregung dankbar. Grüße |
AW: vererbende Klasse oder Interface? oder beides?
Ob besser oder schlechter kann man hier nicht sagen. Das kommt wesentlich auf den Rest der Anwendung an. Interfaces bieten Referenzzählung, blähen den Code aber auch etwas auf. Wenn du alles auf einen gemeinsamen Vorfahren zurückführen kannst, ist eine abstrakte Klasse auch keine schlechte Wahl.
Die Getter und Setter kannst du problemlos als abstract deklarieren. Die abgeleitete Klasse braucht dann auch nicht das Property nochmal zu deklarieren - es genügt, wenn die Getter und Setter überschrieben werden. |
AW: vererbende Klasse oder Interface? oder beides?
Hallo,
Interfaces bieten zwei grundlegende Vorteile: 1. Durch die Referenzzählung müssen Sie nicht explizit freigegeben werden. 2. Ein Interface kann von verschiedenen Objekten implementiert werden, die gerade nicht über einen gemeinsamen Vorfahren verfügen. Da Du in Deinem Beispiel den gemeinsamen Vorfahren TMessgerät hast, denke ich, dass Du keine Interfaces benötigst. In Deinem Beispiel kannst Du übrigens bereits das TMessgerät mit dem Interface ISchreibenLesen verbinden, da die Erben es ohnehin bereitstellen sollen. -- Viele Grüße, Roman Kassebaum Embarcadero Technology Partner Embarcadero MVP Blog: ![]() |
AW: vererbende Klasse oder Interface? oder beides?
Ok, Danke. Das war schon mal sehr interessant.
Wenn ich das Interface ISchreibenLesen mit der Oberklasse TMessgeräte verbinde, dann müsste ich doch eigentlich die Methoden auch in TMessgeräte implementieren. Das möchte ich ja aber erst in den abgeleiteten Klassen. Also müsste ich ja folglich die vom Interface bereitgestellten Methoden als abstract deklarieren. In meinem Fall ist das vielleicht zuviel des Guten und ich deklariere die Methoden gleich in TMessgeräte ohne das Interface. Ansonsten wäre ja 3-mal eine Methode deklariert (im Interface, Oberklasse, Unterklasse) + die Implementierung. Das kann schon auch unübersichtlich werden. Werd mich mal ran machen das umzusetzen! |
AW: vererbende Klasse oder Interface? oder beides?
Genau das gefällt mir auch nicht wirklich. Die Schuld liegt da irgendwie eher an der Delphi-Sprache selber, "echte" abstrakte Klassen kennt es nicht.
Delphi-Quellcode:
INeustartbar = interface
['{C952DFE2-65A3-48A4-8681-81F97946DB5A}'] procedure restart(); end; ISchreibenLesen = interface ['{DBAF7078-C915-471F-8C37-F679253220BE}'] procedure writeStuff(); procedure readStuff(); end; ( AMessgerät = class abstract(TInterfacedObject, ISchreibenLesen) public // Warum ist das hier überhaupt notwendig? procedure readStuff; virtual; abstract; procedure writeStuff; virtual; abstract; end; TMessgerät1 = class(AMessgerät) procedure spezialKram(); end; TMessgerät2 = class(AMessgerät, INeustartbar) public procedure spezialKram(); procedure restart(); end; |
AW: vererbende Klasse oder Interface? oder beides?
Ich hatte ja gestern schon gesagt, dass man Interfaces sehr gut nutzen kann, um Objekte, die keinen gemeinsamen Vorfahren habe, anzusprechen.
Für die zweite Aufgabe, die Referenzzählung, kann man übrigens auch einen ObjGuard ( ![]() Was mir an der vorgestellten Lösung überhaupt nicht gefällt, ist, dass man von TInterfacedObject ableiten muss. Dadurch "weiß" TMessgerät bereits, dass die abgeleiteten Klassen Interfaces nutzen werden. Ich würde also die Interfaces weglassen. -- Roman Kassebaum Embarcadero Technology Partner Embarcadero MVP Blog: ![]() |
AW: vererbende Klasse oder Interface? oder beides?
Delphi-Quellcode:
Das geht genauso. Das Interface muss man ja nicht in der Oberklasse angeben.
INeustartbar = interface
['{C952DFE2-65A3-48A4-8681-81F97946DB5A}'] procedure restart(); end; ISchreibenLesen = interface ['{DBAF7078-C915-471F-8C37-F679253220BE}'] procedure writeStuff(); procedure readStuff(); end; TMessgerät = class abstract(TInterfacedObject) end; TMessgerät1 = class(TMessgerät, ISchreibenLesen) procedure readStuff; procedure writeStuff; procedure spezialKram(); end; TMessgerät2 = class(TMessgerät, ISchreibenLesen, INeustartbar) public procedure readStuff; procedure writeStuff; procedure spezialKram(); procedure restart(); end;
Delphi-Quellcode:
(Wobei das Objekt nach der Methode durch das Interface freigegeben wird.)
var
a: TMessgerät; b: ISchreibenLesen; begin a := TMessgerät2.Create; b := a as ISchreibenLesen; b.readStuff; end; // EDIT: Zitat:
|
AW: vererbende Klasse oder Interface? oder beides?
Es ist ganz wichtig, dass Du die Objektinstanz nicht in einer Variablen merkst. Sobald die Referenzzählung das Objekt freigibt, wäre die Instanz nicht mehr gültig.
Delphi-Quellcode:
--
var
b: ISchreibenLesen; begin b := TMessgerät2.Create; b.readStuff; end; Roman Kassebaum Embarcadero Technology Partner Embarcadero MVP Blog: ![]() |
AW: vererbende Klasse oder Interface? oder beides?
Außer zur Initialisierung in einer lokalen Variable, da finde ich das in Ordnung. Aber sobald das Objekt das erste Mal in einer Interfacereferenz gelandet ist, darf man die Objektvariable natürlich nicht mehr nutzen.
Deshalb ist es auch besser entweder Interfaces oder Objekte zu nutzen und das durch die gesamte Vererbung durch. Oder man deaktiviert die Referenzzählung. |
AW: vererbende Klasse oder Interface? oder beides?
Ich würde Datentransport und Datenaufbereitung strikt voreinander trennen.
Das bedeutet dass es eine Klasse für GPIB gibt die rein nur für den Datentransport zuständig ist und eine weitere (Basis-)Klasse TMessGeraet die für die Aufbereitung der Daten verantwortlich ist. Dabei benützt die Klasse TMessGeraet ihren Kollegen TGPIB. ( ![]() Es liegt also weder eine Vererbung vor noch benötigt man ein Interface. Man könnte sogar eine weitere Klasse für das Datenprotokoll einsetzen falls ein einheitliches Protokoll wie CS-80 (Command Set 80) benützt wird. Man kann diesen Ansatz mit folgender Kette vergleichen:
Code:
TDatabase sorgt für den Datentransport
TDatabase <- TQuery <- TDBEdit
TQuery entspricht praktisch dem Protokoll auf Datenbankebene und TDBEdit repräsentiert die Daten auf Anwendungsebene. Jede Klasse dient nur einem bestimmten Zweck wobei die Klassen miteinander kollaborieren und in einer "benützt"-Beziehung stehen. Wichtig ist noch die Pfeilrichtung der Abhängigkeiten zu beachten. Die Klasse TGPIB darf die Klasse TMessGeraet nicht kennen; umgekehrt kennt und benützt aber TMessgeraet die Klasse TGPIB. |
AW: vererbende Klasse oder Interface? oder beides?
Zitat:
@sx2008 Also der Datentransport ist schon in ein eigenen Klasse mit direktem Zugriff auf den GPIB-Bus.
Delphi-Quellcode:
Also diese Klasse TGPIB weiß auch nichts von anderen Klassen.
procedure send (addr : integer; s : AnsiString; var status : integer);
procedure enter (var s : Ansistring; maxlen : word; var len : word; addr : integer; var status : integer); procedure ieee488_send (addr : longint; c : Pointer; len : longint; var status : longint) stdcall; external 'IEEE_32M.DLL' name 'IEEE488_SEND'; procedure ieee488_enter (c : Pointer; maxlen : longint; var len : longint; addr : longint; var status : longint) stdcall; Nur sind eben die Befehle für die Messgeräte unterschiedlich, z.B. Agilent und Keithley.
Delphi-Quellcode:
Für jede Unterklasse muss man also fast alle Methoden implementieren. Den Vorteil den ich sehe, ist dass man einmal Methodennamen festlegt (auf Höhe der abstrakten TMessgeräte-Klasse), die dann immer wieder gebraucht werden und man so eine einheitliche Syntax über alle Messgeräte hat.
procedure send (22, 'ID?', status); //Agilent
procedure send (22, 'IDN?', status); //Keithley Vergleichen kann man das vielleicht mit der IVIDmm Klasse zur Ansteuerung von Messgeräten. Die besitzt auch ganz allgemeine Methoden Initialize, Close, Configure und Propertys Measurement.Read usw. Was am Ende für ein Messgerät dran hängt ist egal. Ob es sich bei mir wirklich so allgemeingültig durchführen lässt, bin ich gerade am probieren. |
AW: vererbende Klasse oder Interface? oder beides?
Zitat:
In der Basisklasse führst du eine virtuelle Funktion ein die eine Kennung in einen Befehl umsetzt:
Delphi-Quellcode:
Anstelle der Konstanten kann man auch einen Aufzählungstyp verwenden.
const
QUERYSTATUS = 1; RESETDEVICE = 2; // usw. function TMessgeraet.GetCommandString(commandid:Integer):string; // virtual, protected begin raise Exception.CreateFmt('command %d not implemented (%s)', [commandid, Classname]); end; function TMessgeraetAgilent.GetCommandString(commandid:Integer):string; // override; begin case commandid of QUERYSTATUS: Result := 'ID?'; RESETDEVICE: Result := ...; else result := inherited GetCommandString(commandid); end; |
AW: vererbende Klasse oder Interface? oder beides?
Zitat:
Dieses Interface kann dann auf die zur Initialisierung notwendigen Routinen nicht zugreifen und es gibt auch kein anderes Interface, auf das man dafür casten könnte. Theoretisch kann man auch hinterher noch auf den Objekttyp casten, sofern man sich im gleichen Modul der Anwendung befindet (selbe Exe, selbe DLL, ..), aber das sollte bei sauberer Implementierung aller Schnittstellen nicht notwendig sein. |
AW: vererbende Klasse oder Interface? oder beides?
@sx2008
interessante Idee. Werd ich mal im Hinterkopf behalten. @jaenicke danke für die Antwort. Vielleicht werd ichs demnächst verwenden. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:17 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