templates aus interfaces: guter stil?
-
ich steh grad vor dem problem, dass ich eine klasse mit templates hab,sie aber austauschbar sein soll. Diese austauschbarkeit will ich über ein Interface bewerkstelligen,das problem ist nur, dass im interface eine methode enthalten sein muss, die den wert der templatierten variable ausspucken soll,also muss dieses interface auch templatiert werden.
hier der beispielcode, von dem ich nun nicht weis, wiegut der stil ist.template<class T> class Interface{ public: virtual T getobjekt()=0; }; template<class T> class BaseObjekt:public Interface<T>{ //... public: T getObjekt{ return Objekt;} }; //in der main Interface<T>= new BaseObjekt<T>;
das sieht erstens nicht gut aus, zweitens scheint es auch das open-closed-prinziple zu missachten, aber ich hab ehrlich gesagt keine Ahnung, wie ich das anders machen kann.
-
Warum willst du das machen?
Reicht dir statische Polzmorphie nicht? Wenn ja, dann erklaer mal warum und wieso.
-
wer sagt, dass ich das machen will? ich finds selber unschön...
zu dem was ich machen will:
ich habe eine Klasse die bestimmte objekte(in meinem fall texturen, aber das system soll später ausgeweitet werden) verwaltet. Da jedes dieser verwalteten objekte aus platzgründen einmalig sein soll, lass ich andere Objekte nur über pointer drauf zugreifen, wenn kein pointer mehr au das objekt zugreift(das objekt also nichmehr benötigt wird) wird es automatisch gelöscht.
dies soll durch smart pointer sichergestellt werden,da diese pointer aber später sehr unterschiedliche einsatzgebiete haben sollen, kann ich kein einheitliches framework für sie aufbauen, und muss deshalb interfaces benutzen.
Dieses interface hat 2 methoden.
die eine heisst release,sie ruft einen function pointer auf, der der verwaltungsklasse mitteilt, dass nun eine Klasse weniger auf das objekt zugreift.
die andere ist getobjekt(), die einfach nur das objekt, bzw den pointer auf das objekt zurückgeben soll.da diese objekte aber unterschiedliche typen haben, muss getobjekt im interface auch als template existieren..oder gibts da nen anständigen workaround?
grafisch dargestellt :
------------------- ------------- |Verwaltungsklasse| *objekt | pointer | | [e]darr[/e][e]uarr[/e] |------------------------->| | | objekt |<-------------------------| | | | *objRelease() | | ------------------- ------------- ^ | ^ |Requestobj() | | | | | ------------------- getObjekt() | | | Factoryclass |<----------------------------| | | |-------------------------------| ------------------- Release()
-
otze schrieb:
das sieht erstens nicht gut aus, zweitens scheint es auch das open-closed-prinziple zu missachten
Inwiefern missachtet das deiner Meinung nach das OCP?
Ich sehe ehrlich gesagt das Problem nicht (mal davon abgesehen, dass deinem Interface eine virtueller Destruktor fehlt).
Prinzipiell kann die Kombination von statischem und dynamischem Polymorphismus zu sehr flexiblen Lösungen führen. Ein schönes Beispiel dafür findet man z.B. in der Visitor-Implementation der Loki-Lib. Auch für eine generische Observer-Implementation könnte ich mir eine solche Vorgehensweise vorstellen.
-
1. They are “Open For Extension”.
This means that the behavior of the module can be extended. That we can make
the module behave in new and different ways as the requirements of the application
change, or to meet the needs of new applications.flexibel ist das system, aber veränderungen können nachfolgende programmteile negativ beeinflussen, bzw es funktioniert garnicht, mit einem nicht templatiertem interface ist das nicht der fall.
-
otze schrieb:
1. They are “Open For Extension”.
This means that the behavior of the module can be extended. That we can make
the module behave in new and different ways as the requirements of the application
change, or to meet the needs of new applications.flexibel ist das system, aber veränderungen können nachfolgende programmteile negativ beeinflussen, bzw es funktioniert garnicht, mit einem nicht templatiertem interface ist das nicht der fall.
Ich verstehe nach wie vor nicht was du meinst. Wenn du ein Modul gegen ein Interface programmierst, spielt es für das Modul überhaupt keine Rolle ob dieses Interface nun aus einem Template generiert wurde oder nicht.
Ob du nun ein Interface FooInt hast das eine Methode int bar() deklariert oder aber ein Interface Foo<T> das eine Methode T bar() deklariert macht aus Sicht des OCPs keinen Unterschied. Einmal hast du halt ein Modul das auf die Abstraktion FooInt und einmal eins das auf Foo<int> angewiesen ist. In beiden Fällen hoffst du, dass deine Abstraktion stabiler ist als die konkreten Ausprägungen. Wenn diese Annahme nicht hält, hast du verloren. Unabhängig davon ob du ein Template verwendest oder nicht.Also was meinst du?
-
für das modul selber macht es keinen unterschied, aber für nachfolgende klassen schon, denn die müssen sich damit rumplagen, dass aus einem Interface<int> ein Interface<float> wird.
um es nochmal ganz deutlich zu machen: das ocp ist darauf ausgelegt, dass es klassen egal sein kann, was hinter ihnen passiert,das funktioniert natürlich nur solange das Interface welches sie anspricht stabil ist, der sinn des ocp ist ja auch, dass ein interface->foo() angewendet werden kann, ohne dass die klasse sich drum kümmern muss, was genau passiert, sobald die klasse aber merkt, dass foo() verschiedene ergebnistypen liefern kann,hat sie ein dickes problem, sie ist aufeinmal wieder von dem was hinter ihr passiert abhängig.
-
für das modul selber macht es keinen unterschied, aber für nachfolgende klassen schon, denn die müssen sich damit rumplagen, dass aus einem Interface<int> ein Interface<float> wird.
Wir drehen uns im Kreis. Wenn sich dein Interface von Interface<int> nach Interfac<float> ändert ist alles verloren, da sich deine *Abstraktion* ändert auf die du gebaut hast. Das ist aber *völlig unabhängig* von Templates. Wenn sich dein Interface InterfaceInt zu InterfaceFloat wird, hast du genau das selbe Problem.
Du kannst nicht auf der einen Seite sagen: "Hallo, hier habe ich diese fünfzehn verschiedenen Objekte. Mir wäre es allerdings lieb, wenn ich den konkreten Typ der Objekte ignorieren und sie alle über ein abstraktes Interface behandeln könnte." nur um dann im nächsten Moment zu sagen: "So, jetzt wo ich den konkreten Typ ignoriert habe würde ich gerne den konkreten Typ jeden einzelnen Objekts kennen und lierfern können". Das geht natürlich nicht.Du musst hier mal zwei orthogonale Dinge unterscheiden. Die dynamische Polymorphie auf der einen Seite, die dir im OCP-Zusammenhang die möglichkeit zur Abstraktion bietet. Und die statische Polymorphie (Templates) auf der anderen Seite, die dir zur Compilezeit ermöglicht verschiedene Instanzen einer Klasse zu generieren ohne dass du Code schreiben musst. Das eine ist unabhängig vom anderen. Du kannst auf beide das OCP anwenden. Nur verwechseln darfst du das eine und das andere nicht.
Das abstrakte Interface bietet dir die Möglichkeit neue Typen die dieses Interface implementieren ohne Codeänderung und Neukompilation an eine vorhandene Klasse/Modul die Abstraktion baut zu übergeben.
Die Verwendung von Templates auf der anderen Seite bietet dir die Möglichkeit Code zu ändern ohne den Code zu ändern
Du musst also nicht die Klasse/das Modul umschreiben sondern lediglich den konkreten Templateparameter ändern. Das hat aber natürlich eine Neukompilation zur Folge und setzt voraus, dass alle benutzenden Module/Klassen ebenfalls als Template implementiert sind.Was du nicht machen kannst ist Typinformationen zugunsten von virtuellen Methoden aufgeben und dann im nächsten Moment die Typinformationen zurückfordern.
um es nochmal ganz deutlich zu machen: das ocp ist darauf ausgelegt, dass es klassen egal sein kann
Oh. Das OCP ist mir durchaus bekannt. Dennoch verstehe ich nach wie vor dein Problem nicht.
Diese austauschbarkeit will ich über ein Interface bewerkstelligen,das problem ist nur, dass im interface eine methode enthalten sein muss, die den wert der templatierten variable ausspucken soll,also muss dieses interface auch templatiert werden.
Du bist also auf einen *konkreten* Typen angewiesen. Dann ist es doch nur logisch, dass du diese *konkrete* Abhängigkeit nicht über eine allgemeine Abstraktion ausdrücken kannst. Diese Unstabilität resultiert aber nicht aus der Verwendung von Templates sondern daher, dass du den *konkreten* Typen brauchst. Du kannst diese Abhängigkeit nicht über dynamische Polymorphie ausdrücken. Sprich: Eine Änderung des *konkreten* Typs hat auf jeden Fall eine Neukompilation zur Folge.
Templates können dir aber dabei helfen, dass du in diesem Fall anderen Code nicht neuschreiben musst.Ich denke dein Problem ist mir jetzt klar, den Zusammenhang mit dem OCP sehe ich nach wie vor nicht.
-
otze schrieb:
zu dem was ich machen will:
ich habe eine Klasse die bestimmte objekte(in meinem fall texturen, aber das system soll später ausgeweitet werden) verwaltet. Da jedes dieser verwalteten objekte aus platzgründen einmalig sein soll, lass ich andere Objekte nur über pointer drauf zugreifen, wenn kein pointer mehr au das objekt zugreift(das objekt also nichmehr benötigt wird) wird es automatisch gelöscht.
dies soll durch smart pointer sichergestellt werden,da diese pointer aber später sehr unterschiedliche einsatzgebiete haben sollen, kann ich kein einheitliches framework für sie aufbauen, und muss deshalb interfaces benutzen.
Dieses interface hat 2 methoden.
die eine heisst release,sie ruft einen function pointer auf, der der verwaltungsklasse mitteilt, dass nun eine Klasse weniger auf das objekt zugreift.
die andere ist getobjekt(), die einfach nur das objekt, bzw den pointer auf das objekt zurückgeben soll.da diese objekte aber unterschiedliche typen haben, muss getobjekt im interface auch als template existieren..oder gibts da nen anständigen workaround?
Ich sehe hier prinzipiell zwei sinnvolle Möglichkeiten:
A) *Eine* heterogene Liste für alle Objekte.
*Mehrere* homogene Listen. Eine für jede Sorte von Objekten.
Entweder alle Objekte die du verwalten willst erben von einer gemeinsamen Basisklasse. In diesem Fall kannst du *eine* Verwalter-Klasse implementieren die ausschließlich die Basisklassenschnittstelle verwaltet.
Oder, wenn du keine gemeinssame Basisklasse verwenden kannst, dann brauchst du mehrere Verwalter. In diesem Fall wird deine Verwalter-Klasse ein Template und jede Instanz kann genau einen Typ von Smart-Pointern
-
die sache mit der verwalterklasse is mir schon klar, und templateinterfaces benutz ich inzwischen auch, nur gefällt es mir vom aussehen nicht, und bisher hab ich immer die erfahrung gemacht,dass nichts was nicht gut aussieht gut sein kann
-
Versteh dich schon irgendwie, mir kraeuseln sich auch irgendwie die fussnaegel wenn ich an ein Interface uber ein Template denke
Nen Interface sollte in erster linie "Stabil" sein, was meiner meinung nach durch das template aufgeweicht wuerde. Sinn eines Interfaces ist es, Objecte unterschiedlicher Art gleich ansprechen zu koennen. Mit nem Template erreichst genau das gegenteil, das trotz hoher gleichheit (haben viele gemeinsame Funktionen) die Interface der unterschiedlichen typen nicht mal typgleich sind. Das bedeutet, dynamische Polymorphie kannst total vergessen, und kannst nur noch auf statische Polymorphie zurueckgreifen.Zu deinem Spezifischen Problem, ich wuerd das Interface rausloesen.
Also alles was nicht vom Typ abhaengig ist (AddRef, Release kommt mir irgendwie bekannt vor), erst mal in ein Interface. Da solltest alles drin haben, was fuer die eigentliche Verwaltung brauchst.
Dann wuerd ich allen Typen die du speichern willst, nen eigenes Interface verpassen (abgeleitet vom Verwaltungs - Interface) ... ITexture z.b. Und nur ueber ITexture solltest auf deine Nutzdaten (Zeiger) rankommen. Wenn dannoch viel Humor ueber hasst, implementiere nen Mechanismuss zum sicheren Typcasten zwischen den Interfaces ohne doe RTI zu verwenden ....
Bei der Implementation bekommst auch sehr viel gelegenheit, templates einzusetzen ....Ciao ...