Sowohl die Klassenvererbung als auch die Schnittstellen haben ihren Platz. Vererbung bedeutet "ist ein", während eine Schnittstelle einen Vertrag bereitstellt, der definiert, wie sich etwas "verhält".
Ich würde sagen, dass die häufigere Verwendung von Schnittstellen überhaupt keine schlechte Praxis ist. Ich lese gerade "Effective C # - 50 Specific Ways to Improve Your C #" von Bill Wagner. In Artikel 22 heißt es, und ich zitiere "Definieren und Implementieren von Schnittstellen der Vererbung vorziehen".
Im Allgemeinen verwende ich Basisklassen, wenn ich eine bestimmte Implementierung des gemeinsamen Verhaltens zwischen konzeptionell verwandten Typen definieren muss. Häufiger benutze ich Schnittstellen. Eigentlich beginne ich normalerweise damit, eine Schnittstelle für eine Klasse zu definieren, wenn ich damit beginne, eine zu erstellen. Auch wenn ich die Schnittstelle am Ende nicht kompiliere, finde ich, dass es hilfreich ist, zunächst die öffentliche API der zu definieren Klasse von Anfang an. Wenn ich feststelle, dass ich mehrere Klassen habe, die beide die Schnittstelle implementieren, und die Implementierungslogik identisch ist, werde ich mich nur dann fragen, ob es sinnvoll wäre, eine gemeinsame Basisklasse zwischen den Typen zu implementieren.
Ein paar Zitate aus Bill Wagners Buch ...
Alle abgeleiteten Klassen berücksichtigen dieses Verhalten sofort. Durch das Hinzufügen eines Members zu einer Schnittstelle werden alle Klassen getrennt, die diese Schnittstelle implementieren. Sie enthalten die neue Methode nicht und werden nicht mehr kompiliert. Jeder Implementierer muss diesen Typ aktualisieren, um das neue Mitglied aufzunehmen. Die Wahl zwischen einer abstrakten Basisklasse und einer Schnittstelle ist eine Frage, wie Sie Ihre Abstraktionen im Laufe der Zeit am besten unterstützen können. Schnittstellen sind fest: Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie enthalten die neue Methode nicht und werden nicht mehr kompiliert. Jeder Implementierer muss diesen Typ aktualisieren, um das neue Mitglied aufzunehmen. Die Wahl zwischen einer abstrakten Basisklasse und einer Schnittstelle ist eine Frage, wie Sie Ihre Abstraktionen im Laufe der Zeit am besten unterstützen können. Schnittstellen sind fest: Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie enthalten die neue Methode nicht und werden nicht mehr kompiliert. Jeder Implementierer muss diesen Typ aktualisieren, um das neue Mitglied aufzunehmen. Die Wahl zwischen einer abstrakten Basisklasse und einer Schnittstelle ist eine Frage, wie Sie Ihre Abstraktionen im Laufe der Zeit am besten unterstützen können. Schnittstellen sind fest: Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. "
"Coding Interfaces bieten anderen Entwicklern eine größere Flexibilität als das Codieren in Basisklassentypen."
"Die Verwendung von Schnittstellen zum Definieren von APIs für eine Klasse bietet eine größere Flexibilität."
"Wenn Ihr Typ Eigenschaften als Klassentypen verfügbar macht, macht er die gesamte Schnittstelle für diese Klasse verfügbar. Mithilfe von Schnittstellen können Sie festlegen, dass nur die Methoden und Eigenschaften verfügbar gemacht werden, die Clients verwenden sollen."
"Basisklassen beschreiben und implementieren gemeinsames Verhalten über verwandte konkrete Typen hinweg. Schnittstellen beschreiben atomare Funktionalitätsteile, die nicht verwandte konkrete Typen implementieren können. Beide haben ihren Platz. Klassen definieren die Typen, die Sie erstellen. Schnittstellen beschreiben das Verhalten dieser Typen als Funktionalitätsteile. Wenn Sie die Unterschiede verstehen, werden Sie ausdrucksstärkere Designs erstellen, die angesichts von Änderungen widerstandsfähiger sind. Verwenden Sie Klassenhierarchien, um verwandte Typen zu definieren.