Zitat von
Andreas H.:
abstrakte Klassen, Interface: gehört habe ich diese Begriffe schon.
Ok, abstrakt sind Methoden, die einfach noch nicht implementiert werden. Du legst die Signatur (name, Parameter, Rückgabewert) fest und sagst eben, dass diese Methode in dieser Klasse existiert, die Implementierung jedoch lässt Du weg. Erst die Nachfahren (die erben von dieser Klasse) implementieren dann diese Methoden. Hier solltest Du Dir einfach die Objekt Orientierte Programmierung (
OOP) anschauen, dann wird Dir der Nutzen schnell klar.
Einfach gesagt sind die erbenden Klassen nur spezieller als die Elternklasse. Das heißt, überall wo Du eine Instanz der Elternklasse verwenden kannst, kannst Du erst recht einen Nachfahren verwenden, da die Funktionalität erhalten bleibt.
Am Beispiel:
Delphi-Quellcode:
type
TElternKlasse = abstract class(TObject) // Vor Delphi 2006 das abstract an dieser Stelle weglassen
public
procedure doFoo; virtual; abstract; // hier ist virtual; abstract; unabhängig von der Delphi Version
end;
TNachfahreA = class(TElternKlasse);
public
procedure doFoo; override;
procedure doA;
end;
TNachfahreB = class(TElternKlasse);
public
procedure doFoo; override;
procedure doB;
end;
...
procedure TNachfahreA.doFoo;
begin
ShowMessage('Nachfahre A wurde gewählt');
end;
procedure TNachfahreB.doFoo;
begin
ShowMessage('Nachfahre B wurde gewählt');
end;
...
Und dann eben bei der Nutzung:
Delphi-Quellcode:
var vk: TVorfahrKlasse;
na: TNachfahreA;
begin
na := TNachfahreA.Create;
na.doFoo;
// zeigt den Dialog mit A
na.doA;
// funktioniert, denk Dir eine Implementierung aus
na.Free;
vk := TNachfahreA.create;
vk.doFoo;
// zeigt den Dialog mit A
vk.doA;
// wird nicht gefunden, da vk vom Typ TVorfahrKlasse!
vk.Free;
vk := TNachfahreB.create;
vk.doFoo;
// zeigt den Dialog mit B
vk.Free;
// sollte nicht klappen, immer böse!!!
vk := TVorfahrKlasse.Create;
vk.doFoo;
// spätestens hier gibt es eine Exception, da keine Implementierung bekannt ist!
vk.Free;
end;
Das wichtige ist dabei, dass Du zwar eine Instanz von NachfahreA und NachfahreB erzeugst, die behandelst Du aber beide nur als TVorfahrklasse. Das siehst Du halt daran, das vk zwar Instanzen von TNachfahreA/B zugewiesen werden, nutzen kannst Du aber nur das, was die Klasse TVorfahrKlasse an Funktionalität zusichert. Zudem kannst Du nur mit den Nachfahren arbeiten, die die Methode doFoo (bzw. alle abstrakten Methoden) auch implementieren.
Das ganze kannst Du dann auch in Deinem Projekt nutzen. Du schaffst einfach eine Klasse, die eben das Werkzeug steuert und eine Ausgabe auf der TListView vornehmen kann. Aufbauend auf dieser Klasse kannst Du die Codebasis erzeugen, die greift einfach auf die Methoden der Basisklasse zurück. Die Methoden, die sich für unterschiedliche Kunden unterscheiden kannst Du dabei einfach als abstrakt definieren. Für jeden Kunden erzeugst Du dann einen Nachfahren, in dem Du nur noch diese abstrakten Methoden überschreibst.
Bleibt noch das Problem, dass Du für jeden Kunden eine Instanz des richtigen Nachfahren erzeugen musst. Nimm einfach das Beispiel mit Nachfahre A und B. Allgemein kannst Du ein Programm entwerfen, dass eben die Methode doFoo von TVorfahrklasse verwenden möchte (die könnte ja z.B. etwas auf der Listview ausgeben). Allgemein wird also nur eine Klasse verwendet, die diese Methode implementiert (wie ist egal). Du weißt auch wann die Methode aufgerufen wird, was sie macht hängt von der konkreten Implementierung ab.
Nun kommt das Fabrikmuster/Factory Pattern (einfach mal googlen) ins Spiel. Dabei handelt es sich um ein Design-Pattern der Gang of Four (GoF). Es handelt sich eben um eine Fabrik, die Dir eine Instanz erzeugt. Eine Fabrikmethode gibt Dir typischer Weise die Instanz einer Klasse zurück, die eben alles abstrakte implementiert. Für das Beispiel köbnnte eine Fabrik Dir also eine Instanz von TVorfahrKlasse zurückgeben. Der Vorteil ist, dass die Fabrik von der tatsächlichen Implementierung abstrahiert. Du rufst einfach nur die Fabrikmethode auf und ob die Fabrik Dir nun eine neue TNachfahreA, TNachfahreB oder sonst was zurück gibt ist egal.
So kannst Du die gleiche Codebasis für alle Kunden verwenden und musst nur darauf achten, dass die Fabrik abhängig vom Kunden die korrekte Klasse instanziiert (was nur noch Änderungen an einer einzigen Klasse bedingt). Dies kannst Du dann, wie von Bernhard vorgeschlagen, z.B. über eine Konfigurationsdatei steuern (oder auch durch bedingte Kompilierung).