Zitat:
Ich habe ein Interface (oder eine abstrakte Klasse, kommt auf's gleiche raus).
Nein, weil beim Erben eines Interfaces musst du alles implementieren, bei Erben von einer Klasse nicht.
[OT] Ach Kinners, ich liebe dieses Forum, da bekomme ich, ganz egal, wie blöd ich frage, immer eine freundliche und hilfreiche Antwort. Eure Geduld ist echt bewundernswert! (KEINE Ironie!)
Häh? Warum kann ich dann mein Beispiel von oben kompilieren?
Stopp, Irrtum, geht doch nicht. Ich war davon überzeugt, dass es ging. Seltsam...
Dann wäre das Problem ja für Interfaces gelöst. Die müssen immer vollständig implementiert werden.
Wenn ich die richtigen Schlussfolgerungen daraus ziehe und auch das, was jaenicke sagt, richtig interpretiere,
[...]Bei der Implementierung der Ableitung muss man eben schauen was man an Funktionalität anpassen muss und was dafür überschrieben werden muss.[...]
dann bedeutet das, dass ich im Beispiel von oben Folgendermaßen arbeiten muss. Dass ich also das Interface vollständig implementiere, und die Methoden, die ich "noch nicht" brauche, per
abstract
nach oben durchreiche:
Delphi-Quellcode:
type IMyInterface = interface(IInterface)
function getName : string;
function getType : THandlerType;
end;
type TSaveHandler = class(TInterfacedObject, IMyInterface)
protected
procedure SaveFile; virtual; abstract;
function getName2 : string; virtual; abstract;
public
function getType : THandlerType;
function getName : string;
end;
type TSaveTXT = class(TSaveHandler)
protected
procedure SaveFile; override;
function getName2 : string; override; //Result := 'txt'
end;
procedure TSavehandler.getName : string;
begin
Result := getName2;
end;
FOLGEFRAGE DAZU:
Ist es dann nicht sinnvoller, anstatt getName auf das abstrakte getName2 "umzuleiten" (und somit "leeren" Code in TSavehandler zu haben), das Interface aufzutrennen und nur das jeweils nötige zu implementieren?
Delphi-Quellcode:
type ITypeInterface =interface(IInterface)
getType : THandlerType;
end;
type INameInterface =interface(IInterface)
getName : string;
end;
type TSaveHandler = class(TInterfacedObject, ITypeInterface)
protected
procedure SaveFile(_data : TMyData); virtual; abstract;
public
function getType : THandlerType;
end;
type TSaveTXT = class(TSaveHandler, INameInterface)
protected
procedure SaveFile(_data : TMyData); override;
function getName : string; //Result := 'txt'
end;
Oder
handle ich mir dabei weitere/andere Probleme ein?
[edit]
Ich versuche mich mal selbst an einer Antwort:
Wenn ich das INameInterface erst auf der Hierarchie-Ebene von TSaveTXT einführe, verbaue ich mir, die ...Handler "generisch" ansprechen zu können. Was ich damit meine:
Delphi-Quellcode:
type TSaveLibrary =class(TObject) //verwaltet alle vorhandenen SaveXXX
private
FList : TObjectList<TSaveHandler>;
public
procedure SaveDataAs(_qualifier : string; _data : TMyData);
end;
procedure TSaveLibrary.SaveDataAs(_qualifier : string; _data : TMyData);
var i : Integer;
begin
//wobei sich hier auch evtl. ein TDictionary (vergl. http://www.delphipraxis.net/1224610-post12.html) anbieten würde
i := 0;
while ((i < FList.Count) and (FList.Items[i].getName <> _qualifier)) do
Inc(i);
FList.Items[i].SaveFile(_data);
end;
Funktioniert natürlich nur dann, wenn TSaveHandler bereits die getName Methode implementiert, und nicht erst TSaveTXT.
Soll heißen: Sobald ich konkrete Klassen über deren gemeinsamen Vorfahren ansprechen muss, muss dieser Vorfahr die Methoden bereits stellen, auch wenn er selbst damit noch nichtsa anfangen kann. *kopfkratz* War irgendwie offensichtlich, oder?
Sorry für meine Fragen, aber Interfaces sind (bis jetzt) für mich Neuland auf das ich mich noch nicht gewagt habe.
[AB HIER UNINTERESSANTE SELBSTGESPRÄCHE - Unterschied Interface - abstrakte Klasse]
Besonders deswegen, weil mir (bei meinem (!) Kenntnisstand) noch nichts untergekommen ist, wo abstrakte Klassen nicht auch ausreichen würden.
Ja, ich weiß, interfaces und abstrakte Klassen sind bereits durchgekaut worden bis zum Erbr*chen und deswegen will ich eigentlich auch keine weitere Diskussion mehr darum führen.
Hier jedoch gibt es mMn eine so schöne Zusammenfassung, dass ich diese mal nach meinem Verständnis zusammenfassen will.
Mehrfachvererbung: abstrakte Klassen unterstützen KEINE Mehrfachvererbung, Interfaces schon.
Felder und Logik: Abstrakte Klassen können im Vergleich zu interfaces Felder und bereits zusätzliche Logik ("teilabstrakte Klasse") enthalten.
Eigenschaften/Identität: Interfaces sollen die Eigenschaften der implementierenden Klasse festlegen, während abstrakte Klassen die Identität festlegen. Soll heißen meines Verständnis' nach:
Delphi-Quellcode:
type TAuto = class(TInterfacedObject, IDrivable, IRecycable);
private
FMaxSpeed : Integer;
protected
procedure FahreVorwärts; virtual; abstract; //aus IDrivable;
procedure Wiederverwerten; virtual; abstract; //aus IRecycable;
end;
type TPeugeot206 = class(TAuto)
[...]
end;
Die Interfaces IDrivable und IRecycable zeigen an, was TAuto (und alle Nachfahren) machen kann, legen aber nicht genauer fest, auf welche Weise das geschieht.
Es gibt viele Klassen, die mit TAuto nichts gemeinsam haben und dennoch IRecycable implementieren, z.B. TBierflasche, TZahnpastatube
Die abstrakte Klasse TAuto jedoch (als Summe aller Methoden und Felder (?)) legt die Identität aller Nachfahren fest. Ein Nachfahre von TAuto (z.B. TPeugeot206) "ist ein Auto" und nichts anderes mehr, besonders nicht einfach nur "auto-able".
Homogenität/Heterogenität: Wird als Grundlage (z.B. für das Entwickeln von Plugins) eine abstrakte Klasse zur Verfügung gestellt, ist man als Entwickler auf der einen Seite an die Vorgaben (und evtl. in der Klasse bereits existierende Logik) gebunden, was einem auf der einen Seite gewisse Freiheiten nimmt, auf der anderen Seite wird man aber auch an die Hand genommen, da bereits ein Teil der Programmierung für einen erledigt wurde.
Mit anderen Worten: Wenn sich Plugins stark unterscheiden und nur die gleichen Aufrufe teilen, sind Interfaces besser geeignet (Heterogenität). Arbeiten alle Plugins ähnlich, kann mehr Logik in die Basisklasse ausgelagert werden. Hier sind aufgrund der Homogenität abstrakte Klassen besser.
Erweiterbarkeit: Soll später erweitert werden, müssen alle implementierenden Klassen dieses Interfaces geändert werden, während bei einer abstrakten Klasse (eben weil sie Logik enthalten kann) ein default-Verhalten eingebaut werden kann.
Gut, das gibt mir jetzt erst einmal zu denken.
Ich glaube, meine Frage ist für mich hinreichend beantwortet. Danke an alle
Absolutes sorry dafür, dass ich hier so viele Selbstgespräche führe, die für Euch alle nichts Neues bringen.
Aber solange mir etwas nicht absolut Stück für Stück vorgekaut wird, brauche ich recht lange, bis ich es verstehe und der einfachste Weg ist es für mich, alles en détail niederzuschreiben.