Hi,
Gibt es eine gute Regel, wann ich eine abstrakte Klasse nutzen, und wann ich lieber ein Interface benutzen sollte?
Meine Situation:
Ich habe eine MySQL Klasse und eine MsSQL Klasse geschrieben und wollte nun das passende Interface dazu schreiben, um später weitere Klassen schreiben zu können.
Jetzt überlege ich aber, ob eine abstrakte Klasse nicht besser gewesen wäre, da z.B. alle Membervariablen gleich bleiben und ich diese zumindest nicht in den konkreten Klassen schreiben müsste.
Desweiteren müsste ich die Properties nicht wiederholen und einige Methoden sind auch in allen DB Klassen gleich, sodass ich diese nicht zwei mal im Code hätte.
Was meint ihr, soll ich eine abstrakte Klasse machen und dann nur ein paar Methoden überschreiben, oder ein Interface benutzen?
mh...beim Schreiben ist mir eingefallen, dass eine abstrakte Klasse ja auch keine Implementation beinhaltet...warum gibt es denn dann abstrakte Klassen und Interfaces, nur wegen der Variablen?
Gruß Thomas
E: Werden bei abgeleiteten Klasse die usings aus der abstrakten Klasse übernommen? Kann ich diese auch in Interfaces schreiben?
Hallo Thomas B,
wurde schon oft und vor allem ausführlich diskutiert, u.a. in Interface, Klassen und Vererbung ?
herbivore
Ich habe mich durch den Thread durchgelesen und bin nun nicht viel schlauer als vorher, weil mir Interfaces geläufig sind. Ich habe eher ein Problem damit, eine abstrakte Klasse einzuordnen.
Aber ich habe da eine Idee: Ich baue eine Basisklasse SQL und lass sie das Interface ISQL implementieren. Von der Basisklasse leite ich MySQL und MsSQL ab und überscheibe/implementiere die Methoden, die sich unterscheiden?!
Oder doch lieber einfach nur ein Interface nutzen und alles in den konkreten Klassen implementieren? -> doppelter Code
X(
Auch noch offene Fragen aus dem ersten Post:
Werden bei abgeleiteten Klasse die usings aus der abstrakten Klasse übernommen? Kann ich diese auch in Interfaces schreiben?
Hallo Thomas B,
also Interface und abstrakte Klasse gleichzeitig halte ich nicht für sinnvoll.
Wenn du doppelten Code vermeiden willst, kannst du auch eine Hilfsklasse verwenden, die von den anderen Klassen benutzt (also nicht gererbt, sondern eben benutzt) wird.
Werden bei abgeleiteten Klasse die usings aus der abstrakten Klasse übernommen?
Nein!
Kann ich diese auch in Interfaces schreiben?
Nein!
usings sind nur ein Mittel zur Schreiberleichterung und muss in jeder Quell-Datei individuell eingetragen werden. usings sind aber nicht nötig. Es geht immer auch ganz ohne.
herbivore
Hallo,
naja ich würde folgende Regel in etwas befolgen:
Abstrakte Klasse: Jede Unterklasse unterscheidet sich von der Eltern-Klasse nur in wenigen, bestimmten Funktionen.
Des Weiteren sollte die abstrakte Eltern Klasse bereits Code enthalten, die für alle Kinder gleich sind.
Interface: Wenn es keine gemeinsame Codebasis für die Kinder gibt.
Man muss überlegen, wozu man so ein Interface verwenden möchte. Wenn der Code, der hinter einem Interface sehr unterschiedlich seien kann, wie z.B. bei IEnumerable, dann lieber ein Interface.
Hast du aber eine Elternklasse, die Dateien vom Computer A zu Computer B transferieren, und sich nur der Down/Upload unterscheidet (oder evt. der Transportweg), dann lieber eine abstrakte Klasse wählen, und den konkreten Dateitransfer in den Kinderklassen realisieren.
Die Methode der Delegation ist auch eine Möglichkeit, seine Klasse recht leicht zu erweiteren.
Hilfsklasse? Was soll ich mir nun darunter vorstellen? Wie benutze ich denn eine Klasse als Hilfsklasse?
Wie würdet ihr denn sowas machen?
E: NachQWald wäre also eine abstrakte Klasse gut, denn der Code der Klassen unterscheidet sich praktishc nicht, bis auf einem Buchstabe, wenn ich MsSQL Methoden brauche anstelle der MySQL Methoden (vom MySQLClient und MsSQLClient).
Hallo Thomas B,
wie man jede Klasse benutzt. Einfach deren Methoden, die den sonst doppelt benötigten Code enthalten, aufrufen.
herbivore
Hallo Thomas B,
als "berühmte" Hilfklasse ist im .NET Framework z.B. die "Convert" Klasse zu verstehen. Besonders auffällig ist hier z.B. dass die meisten Methoden static sind. Muss aber natürlich nicht.
Gruß
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
Irgendwie seh ich da kaum Überschneidungsbereiche für abstrakte Klassen und Interfaces. Bei abstrakten Klassen heißt es immer das abgeleiteten Klassen auch die abstrakte Klasse sind. Sprich wenn ich ne abstrakte Klasse Figur hab und Kreis und Quadrat davon ableite dann sind das auch Figuren.
Das ist bei Interfaces doch ganz anders. Klassen die bestimmte Interfaces implementieren garantieren dass eine bestimmte Funktionalität vorhanden ist, das heißt nicht das sie auch das gleiche sind logisch gesehen. Nen Interface IFliegen könnte sowohl von ner Klasse für Vögel, als auch von einer Klasse für Flugzeuge implementiert werden und beide hätten außer der Fähigkeit zu fliegen nicht viel gemein.
In deinem Fall würde ich Interfaces verwenden um eine bestimmte Funktionalität zu garantieren. Um wirklich die Vorteile der verschiedenen Datenbanksysteme ausnutzen zu können, würde ich nicht versuchen die auf nen gemeinsamen Nenner in der Implementierung zu bringen.
Baka wa shinanakya naoranai.
Mein XING Profil.
Ich bin nun dabei, das Ganze mit einem Interface zu machen und habe nun zwei Probleme.
Ich habe die SQL Klassen als Singleton umgesetzt mit entsprechende statischer Methode um ein Objekt zu erhalten. Ich habe diese Methode aus dem Interface entfernt, weil ich nicht im Interface festlegen kann, welchen Rückgabewert die Klasse hat, da jede Datenbankklasse einen anderen ückgabewert hat.
Ähnliches Problem wie 1.
void AddParameter(String pParameter, MySqlDbType pDatabaseType, int pSize) { }
Dies ist aus der MySQL Klasse und wie man sieht ist MySQLDbType wieder Klassen spezifisch...aber wie gehe ich in solchen Situatioen vor, um sie für ein Interface nutzen zu können?
Zu 1.
du kannst doch theoretisch sowas schreiben:
public interface ITest {
ITest GetObject();
}
aber! in Interfaces kannst du keine statischen Methoden vorschreiben. Dafür müsstest du abstrakte Klassen nehmen 🙂
zu 2.
Wenn du Datenbankunabhängig schreiben möchtest, deshalb benutzt man ja das Interface, solltest du auch Datenbankunabhängige Datentypen benutzen die erst in der entsprechenden Interface Implementation umgemappt werden auf den datenbankspezifischen Typen. Z.B.(willkürlich konstruiert) hast du ne Funktion die als Parameter nen Typ DBInteger erwartet, in der MySQL Implementation würdest du den intern als Integertyp vom MySQL Server verwenden und im MSSQL Server dann als Integertyp vom MSSQL Server.
Baka wa shinanakya naoranai.
Mein XING Profil.
Prima hat jetzt geklappt das Kompilieren, aber nun ein neues Problem. Ich habe das Interface als DLL kompiliert und diese in den Standard Ordner belassen. In meiner MySQL Klasse habe ich nun diese DLL den Verweisen hinzugefügt. Versuche in nun using NLC.Data.ISQL zu machen und MySQL : ISQL, dann kennt er ISQL nicht, und den Namespace NLC.Data.ISQL findet er auch nicht.
An sich müsst so gehen, verweis auf die DLL mit dem Interface und dann implementieren. Hast du das Interface auch public gemacht?
Baka wa shinanakya naoranai.
Mein XING Profil.
Kein Kommentar dazu schäm
😉
Hallo Thomas B,
das wird Dir auch in 10 Jahren noch passieren, wenn VS dann immer noch standardmäßig Interfaces als private durchs Template generiert. Passiert mir auch immer wieder. Inzwischen kenne ich aber entsprechende Fehlermeldung zur Genüge, und ich weiß, dass ich dann dort zu erst schauen muss.
Also kein Grund zum Schämen 😉
Ciao
Norman-Timo
A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”
Interfaces werden verwendet um öffentliche Schnittstellen zu definieren. Abstrakte Klassen hingegen sollten verwendet werden, um gemeinsame Implementierungen oder gemeinsame interne Schnittstellen vorzugeben.
Generell führt Vererbung zu einer Kopplung der Klassen. Insofern sollten Basisklassen mit Bedacht eingesetzt werden, weil sie das System monolithisch machen.
Alternative zu Basisklassenimplementierungen kann auch das von Herbi genannten Strategie-Muster sein. Auch dieses hat gegenüber der Vererbung den Vorteil, dass keine Kopplung entsteht.
In vielen Lehrbüchern der OO wird intensiv Polymorphie am Beispiel Vererbung gezeigt. Man muss aber sagen, dass dieses Vorgehen heutzutage eher Old School ist. Vererbung wird heute wesentlich kritischer gesehen und seltener eingesetzt, als noch vor 10 Jahren. Liegt aber auch daran, dass die Anforderungen an Softwarearchitekturen sich verändert haben. Flexibilität hat einen wesentlich höheren Stellenwert bekommen. Moderne Sprachen tragen dem Rechnung, indem sie Mehrfachvererbung nicht erlauben und so automatisch die Verwendung von Interfaces befördern.
hallo,
der thread ist zwar schon etwas älter, hat aber imho. noch nicht an aktualität verloren, deshalb die anmerkung meinerseits. 😃
Ev. hat sich ja auch eure Meinung zu dem Thema geändert?
Bisher bin ich so vorgegangen, das ich für ziemlich alle Klassen ein Interface definiert habe - das wird einem auch in div. Literatur v.a. im Zusammenhang mit TDD empfohlen, weil man dadurch die konkreten Klassen z.B. durch ein DI Framework austauschen lassen kann bzw. bei Unittests einfacher Mock Objekte verwenden kann.
Momentan bin ich am lesen von http://www.amazon.de/Framework-Design-Guidelines-Conventions-Development/dp/0321545613/ref=sr_1_1?ie=UTF8&s=books-intl-de&qid=1229333161&sr=8-1 (Framework Design Guidelines, gutes Buch btw.) und hier wird vom .net Team empfohlen, sehr wohl beim Design von Frameworks auf abstrakte Basisklassen zu setzen.
Grund: Interfaces dürfen nicht mehr verändert werden, sobald das Framework einmal bereitgestellt wurde, da das ja ein "braking change" wäre (is klar).
Bei abstrakten Basisklassen ist es einfacher, Erweiterungen im Nachhinein durchzuführen. Weiters wurde gesagt, dass das .net FW Team bisher immer bessere Erfahrungen mit abstrakten Basisklassen gemacht hat und es schon bereut wurde, dass gewisse Dinge via Interface realisiert wurden.
Es wurde auch als "DO" empfohlen, wenn man ein Interface bereitstellt, auf jeden Fall auch mind. 1 (abstrakte) Klasse bereitzustellen, welche dieses impl., sodass sich Clients aussuchen können, ob sie das Interface impl. wollen od. von der Klasse ableiten.
fg
hannes
Hallo HannesB
Genau so ist es, danke für deinen Zusatz.
Hier noch zwei Posts von Phil Haack (ASP.NET Team bei MS) dazu:
Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011
Hallo HannesB,
naja, beim Design eines Frameworks, dass von Dritten außerhalb des eigenen Einflussbereichs benutzt wird, habe solche Überlegungen sicher ihre Berechtigung. Wenn man den gesamten Code unter Kontrolle (des eigenen Teams oder der eigenen Firma) hat, dann kann man ja auch "breaking changes" durchführen. Wenn man z.B. ein Interface erweitert, muss man eben alle Klassen, die es implementieren, auch erweitern. Das wird in vielen Fällen mit überschaubarem Aufwand zu erledigen sein.
Und einen Nachteil haben abstrakten Oberklassen weiterhin, weshalb man in entsprechenden Fällen nicht um Interfaces drumrum kommt: Eine Klasse kann nur von einer abstrakten Oberklasse erben.
Die Argumentation aus dem Buch ist eher eine technisch-pragmatische. Das ist aber nicht der einzige zu berücksichtigende Aspekt. Auf der Modellebene gibt es einen entscheidenden Unterschied zwischen abstrakten Klassen und Interfaces. Abstrakte Klassen führen zu einer IST-EIN-Beziehung zu ihren Unterklassen, wogegen Interfaces eher einer KANN-AUCH-Beziehung entsprechen. Interfaces beschreiben eben einen Ausschnitt von Eigenschaften und Verhaltensweisen von Klassen, aber nicht was eine Klasse insgesamt ist, wie das abstrakte Klassen tun. Deshalb heißen abstrakte Klassen meistens wie Substantive und Interfaces meistens wie Adjektive. Und das ist auch gut so. Es zeigt, dass abstrakte Klassen und Interfaces unterschiedlichen Einsatzzwecken dienen und das sollte man nicht aufgrund von technischen Überlegungen wegfegen.
herbivore