Ja genau - darum gehts mir
Es kommt darauf an, was man damit erreichen möchte.
Basisklassen nutzt man, wenn eine Grundfunktionalität bereit gestellt werden soll. Davon abgeleitet implementieren spezialisierte Klassen jeweils die für sie notwendige Funktionaltät.
Manchmal weiß man vorher, wann eine Basiklasse angebracht ist, manchmal aber auch erst, wenn man eine zweite, dritte ... Version einer Klasse benötigt. Dann verlagert man die gemeinsame Funktionalität in eine Basisklasse.
Oder eine Library stellt Grundfunktionalitäten in Form von Basisklassen zur Verfügung, z.B. ein GUI-System ein Window, das man mit einer eigenen Subclass spezialisieren kann, indem man im Konstruktor Labels, Pushbuttons, Checkbutton, Listeners etc. hinzufügt.
Bei Interfaces gibt es unterschiedliche Anwendungsszenarien:
a) Wenn Interfaces einen Vertrag definieren: "So mußt Du das machen, sonst weiß ich nichts mit Dir anzufangen". Das sagt der, der den Vertrag vorgibt.
Dabei geht's um Polymorphismus, z.B. ein Listener-, Consumer-, Input/OutputStream-Interface. Der, der das Interface vorgibt, erhält eine Instanz der Interface-implementierenden Klasse und ruft dessen (Interface-)Methoden auf.
Du schreibst z.B. einen Keyboard-Listener, registrierst ihn bei Deinem GUI-System und bekommst jeden Tastendruck mit.
Du instanziierst also Deine Klasse und gibst die Instanz einem Framework, das sie aufruft. Das kann auch Dein eigenes Framework sein.
Das Pattern "Don't call us, we call you" paßt in dieses Anwendungsszenario. Anstatt selbst zyklisch das Keyboard abzufragen, wirst Du vom Framework darüber informiert, ob eine Taste gedrückt wurde.
b) Bei Aufrufschnittstellen baut man gerne Interfaces, vor allem beim Remoting (Client/Server), z.B. bei CORBA oder EJB-Remoting (Java). Niemand möchte auf dem Client die Klassen kennen, die die Serverfunktionalität implementieren, sondern lediglich das Interface zu dieser Serverfunktionalität. Dazu trennt man auch Interface und Implementierierung in zwei Libraries auf. Der Client kennt nur die mit den Interfaces, der Server beide.
c) Inversion of Dependency:
Man möchte in komplexem Fachcode (Bank, Versicherung, ...) mit all seinen unzähligen Fallunterscheidungen nicht auch noch komplexen Code z.B. zum Persistieren drin haben, also Code zum Lesen, Speichern, Ändern und Löschen von Daten. Dann baut man sich für jedes Fachobjekt ein Interface, das Methoden zum Zugriff auf seine Attribute, andere beziehungsnahe Objekte enthält etc. So kann z.B. das Interface Kunde die Methode eroeffneKonto( Konto) enthalten. Der Aufrufer dieser Methode (der komplexe Fachcode) weiß aber gar nicht, wohin mit dem neuen Konto, will es auch gar nicht wissen. Das einzige, das ihn interessiert ist, daß ein Konto eröffnet werden soll. Das Konto-Objekt kann er noch selber erstellen, weiß aber nicht wohin damit, also macht er's sich einfach und definiert die dafür zuständige Methode im Interface Kunde. Jemand anders soll sich darum kümmern. Das ist die Klasse, die das Interface Kunde implementiert. Diese Klasse muß jemand instanziieren, der diesen komplexem Fachcode aufruft. Das macht die über dem Fachcode liegende Schicht usw. usf...
Lange Rede kurzer Sinn: mit Interfaces läßt sich die Abhängigkeit umdrehen, nennt sich Inversion of Dependency. Das macht Sinn bei hochkomplexen fachlichen Anforderungen, die man nicht noch zusätzlich mit Technikkram wie z.B. Datenbankoperation oder XML-Generierung etc. belasten möchte. So einen Mischmaschcode blickt irgendwann keiner mehr. Und: der komplexe Fachcode läßt sich easy peasy unittesten, weil er nur den reinen Fachcode und eben keine Datenbankoperation etc. enthät.
Es gibt auch Mischformen, also Basisklassen, die ein (oder mehrere) Interface implementieren. Das macht man dann, wenn Polymorphismus gefordert ist, aber man davon ausgeht, daß alle Klassen, die dieses Interface implementieren, dieselbe Grundfunktionaltät benötigen.
Man könnte ja.. (mit der Formulierung fangen alle dummen Ideen an), eine
Basisklasse mit leeren Methodenrümpfen schreiben, die dann fleißig in
abgeleiteten Klassen overriden werden.
Fleiß und Zwang, Methoden zu implementieren, ist nicht die Intention von Vererbung oder Interfaces. Es stecken konkrete Konzepte dahinter, siehe oben a-c.
Dann kann man Instanzen der abgeleiteten Klassen im Basisklassentyp
referenzieren.
"this" gibt es in der Sub- und Superclass immer. Die Frage ist, ob "this" überhaupt gebraucht wird.