C++ Singleton-Klassen - Vererbung gute Praxis
In einem vorhandenen Projekt, ich bin Erben eine Controller-Klasse (MVC) - deklariert als Singleton zu definieren, meine eigene Behandlung. Wie angemessen Ableitung dieser Singleton-Klasse?
Erste, die ich erweitern auf den Kontext und die Notwendigkeit für dieses Erbe.
Die Anwendung, die ich Hinzugefügt, um die vorhandene software nutzen will, ein MVC-Modul, das führt fast dieselbe Aufgabe wie die, die ich bereit bin zu führen. Es nutzt die gleichen Methoden, bis auf die Unterschrift und leichten Modifikationen. Umschreiben von meinem eigenen MVC-Modul ist endgültig Duplizierung von code. Das vorhandene Modul ist an und orientiert auf dessen Anwendung zu einem anderen Teil der software, und ich kann einfach nicht verwenden das gleiche Modul. Aber ist geschrieben, wie eine Model-View-Controller-Muster, bei dem Controller ist Singleton. Ich abgeleitete Ansicht schon.
Zweitens habe ich Zweifel, dass ich ja klassisch ableiten Singleton-Klasse.
Aufruf Konstruktor von der Klasse geerbt würden, rufen Sie einfach getinstance() für übergeordnete Klasse und nicht wieder ein Objekt aus der abgeleiteten Klasse (?).
Dritte, ist es, wie ich sehe einige Weg, damit umzugehen. Bitte kommentieren/helfen mich zu verbessern!
Kopiere ich die ganze Singleton-Klasse in einer Klasse, die ich nennen könnte AbstractController. Ich leite diese Klasse zweimal. Das erste Kind, singleton-und nimmt die ganze Behandlung der übergeordneten Klasse. Das zweite Kind ist der Controller für meinen Teil der Anwendung, mit der eigenen Behandlung neu definiert.
Dank!
- Für die meisten Teil, wenn eine Klasse eine Basis-Klasse, die singleton ist, dann ist es völlig nutzlos: man kann nie schaffen, alle Instanzen der abgeleiteten Klasse, da jede solche Instanz würde auch eine Instanz der Basis-Klasse, und du bist nicht erlaubt, um eine weitere einer von denen. (Kann ich mir vorstellen "angemessenen" Ausnahmen, aber nur in dem Fall, wo die Basis-Klasse wurde entwickelt, speziell mit der Idee abgeleitet wird, in den Sinn....)
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich bin mir nicht sicher, ich verstehe die situation, die Sie beschäftigen sich mit voll, und ob oder nicht es möglich oder angemessen ist, um die Ableitung von der singleton-hängt stark davon ab, wie das singleton implementiert ist.
Aber da Sie erwähnt "gute Praxis" gibt es einige Allgemeine Punkte, die in den Sinn kommen beim Lesen die Frage:
Vererbung ist in der Regel nicht das beste Werkzeug zu erzielen, code wieder zu verwenden. Siehe: Bevorzuge Komposition über Vererbung?
Singleton und "gute Praxis" in der Regel nicht zusammen gehen! Siehe: Was ist so schlimm daran, singletons?
Hoffe, das hilft.
Wahrheit ist, singletons und Vererbung spielen nicht gut zusammen.
Yeah, Yeah, das Singleton-Liebhaber und GoF Kult werden alle über mich, sagen: "nun, wenn Sie Ihren Konstruktor geschützt..." und "you don' T haben eine
getInstance
Methode in der Klasse, können Sie put es...", aber Sie sind nur beweist mein Punkt. Singletons haben zu springen durch mehrere Reifen, um beides zu sein, ein singleton, und eine Basis-Klasse.Aber nur um die Frage zu beantworten, sagen wir, wir haben eine singleton-Klasse basieren. Es kann sogar zu einem gewissen Grad der Durchsetzung der Einheitlichkeit durch Vererbung. (Der Konstruktor hat eines der wenigen Dinge, die funktionieren kann, wenn es nicht mehr privat sein: es löst eine Ausnahme aus, wenn ein anderer
Base
bereits vorhanden ist.) Sagen wir, wir haben auch eine KlasseDerived
erbtBase
. Da erlauben wir die Vererbung, lassen Sie uns auch sagen, es kann eine beliebige Anzahl von anderen Unterklassen vonBase
kann oder nicht erbt vonDerived
.Aber es gibt ein problem -- die sehr Sie sind entweder bereits laufen oder bald. Wenn wir
Base::getInstance
zimmerreserviereung, ohne das Sie konstruiert ein Objekt bereits ist, bekommen wir einen null-Zeiger. Wir würden gerne wieder was singleton-Objekt auch vorhanden ist (es kann eineBase
, und/oder eineDerived
, und/oder eineOther
). Aber es ist schwer zu tun, und die noch Folgen alle Regeln, weil es nur ein paar Möglichkeiten, dies zu tun-und Sie alle haben einige Nachteile.Konnten wir erstellen Sie einfach einen
Base
und gibt es zurück. SchraubeDerived
undOther
. Endergebnis:Base::getInstance()
gibt immer genau eineBase
. Die Kind-Klassen nie bekommen zu spielen. Irgendwie Niederlagen der Zweck, IMO.Wir können eine
getInstance
unsere eigenen in unserer abgeleiteten Klasse, und dem Anrufer sagenDerived::getInstance()
wenn Sie möchten gezielt einenDerived
. Dies erhöht deutlich die Kupplung (weil ein Anrufer nun wissen, auf Wunsch speziell eineDerived
, und landet binden sich, dass die Umsetzung).Wir tun könnten, eine Variante, die Letzte-aber anstatt die Instanz, die Funktion einfach erstellt. (Wenn wir schon dabei sind, lasst uns umbenennen die Funktion
initInstance
, da wir nicht besonders kümmern, was es bekommt-wir sind nur nannte es so, dass es schafft eine neueDerived
und Sätze, die als die einzig Wahre Instanz.)Also (sofern keine Ungereimtheit vermisst noch), es funktioniert ein bisschen wie dieses...
- Und in deiner init-code, sagen Sie
Base::initInstance();
oderDerived::initInstance();
je nachdem, welche Art Sie wollen, dass die singleton zu sein. Sie müssen umgewandelt den RückgabewertBase::getInstance()
umDerived
-spezifische Funktionen, natürlich, aber ohne Guss können Sie beliebige Funktionen definiert durchBase
, einschließlich der virtuellen Funktionen überschriebenDerived
.Beachten Sie, dass diese Art zu tun, es hat auch eine Reihe von Nachteilen des eigenen, aber:
Er legt die Last der Durchsetzung der Lauterkeit auf die Basisklasse. Wenn die Basis nicht über diese oder eine ähnliche Funktionalität, und Sie können es nicht ändern, Sie sind irgendwie geschraubt.
Die Basis-Klasse nicht nehmen alle der Verantwortung, wenn-jede Klasse muss erklären, einen Destruktor geschützt, oder könnte jemand kommen und löschen Sie eine Instanz nach der Umwandlung (in)entsprechend, und die ganze Sache geht in die Hölle. Was ist schlimmer, dieser nicht durchgesetzt werden kann, durch den compiler.
Weil wir sind protected-Destruktor um zu verhindern, dass einige zufällige schmuck vom löschen unserer Instanz, es sei denn, der compiler ist schlauer als ich fürchte, es ist auch die Laufzeit nicht in der Lage sein, um richtig zu löschen die Instanz, wenn das Programm endet. Bye bye, RAII...Hallo "memory leak detected" - Warnungen. (Natürlich ist der Speicher irgendwann zurückgefordert werden, die von jedem anständigen OS. Aber wenn der Destruktor nicht laufen, Sie kann sich nicht darauf verlassen, es zu tun-Bereinigung für Sie. Müssen Sie rufen Sie eine bereinigungsfunktion von einer Art, bevor Sie verlassen, und das wird Ihnen nicht annähernd die gleichen Zusicherungen, dass RAII Sie geben kann.)
Es stellt eine
initInstance
Methode, die, IMO, nicht wirklich gehört in eine API, die jeder sehen kann. Wenn Sie wollten, könnten SieinitInstance
privaten und lassen Sie Ihre init-Funktion werden einefriend
dann aber Ihre Klasse macht Annahmen über den code, der außerhalb seiner selbst, und die Kupplung Ding kommt wieder ins Spiel.Beachten Sie auch, dass der code oben ist gar nicht thread-sicher. Wenn Sie das brauchen, Sie sind auf Ihre eigenen.
Ernst, der weniger schmerzhafte Weg ist, um vergessen zu versuchen, zu erzwingen, Ehelosigkeit. Die am wenigsten komplizierte Weg, um sicherzustellen, dass es nur eine Instanz ist nur erstellen ein. Wenn Sie brauchen, um verwenden Sie es mehrere stellen, sollten dependency injection. (Die nicht-framework-version, die Beträge zu "übergeben Sie das Objekt an Sachen, die es braucht". 😛 ) ich ging und entwickelt die oben genannten Dinge, nur um zu versuchen und zu beweisen, mich falsch zu singletons und Vererbung, und nur bekräftigt mich, wie schlecht die Verbindung ist. Ich würde nicht empfehlen jemals tatsächlich tun, ist es in echten code.