Antworten:
Einige Gründe, aus denen Sie möglicherweise einen privaten Konstruktor benötigen:
final
auf Klassenebene setzen. Das Setzen eines privaten Konstruktors ist aus diesem Grund so gut wie nutzlos.
final
. Dies hat Sun für die String-Klasse getan und einen vernünftigen Teil der API, der nicht erweitert werden sollte.
Durch die Bereitstellung eines privaten Konstruktors verhindern Sie, dass Klasseninstanzen an einem anderen Ort als dieser Klasse erstellt werden. Es gibt mehrere Anwendungsfälle für die Bereitstellung eines solchen Konstruktors.
A. Ihre Klasseninstanzen werden in einer static
Methode erstellt. Die static
Methode wird dann als deklariert public
.
class MyClass()
{
private:
MyClass() { }
public:
static MyClass * CreateInstance() { return new MyClass(); }
};
B. Ihre Klasse ist ein Singleton . Dies bedeutet, dass nicht mehr als eine Instanz Ihrer Klasse im Programm vorhanden ist.
class MyClass()
{
private:
MyClass() { }
public:
MyClass & Instance()
{
static MyClass * aGlobalInst = new MyClass();
return *aGlobalInst;
}
};
C. (Gilt nur für den kommenden C ++ 0x-Standard) Sie haben mehrere Konstruktoren. Einige von ihnen sind deklariert public
, andere private
. Um die Codegröße zu reduzieren, rufen öffentliche Konstruktoren private Konstruktoren auf, die wiederum die ganze Arbeit erledigen. Ihre public
Konstruktoren werden daher als delegierende Konstruktoren bezeichnet:
class MyClass
{
public:
MyClass() : MyClass(2010, 1, 1) { }
private:
MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};
D. Sie möchten das Kopieren von Objekten einschränken (z. B. aufgrund der Verwendung einer gemeinsam genutzten Ressource):
class MyClass
{
SharedResource * myResource;
private:
MyClass(const MyClass & theOriginal) { }
};
E. Ihre Klasse ist eine Utility-Klasse . Das heißt, es enthält nur static
Mitglieder. In diesem Fall darf niemals eine Objektinstanz im Programm erstellt werden.
Eine "Hintertür" verlassen, die es einer anderen Freundklasse / Funktion ermöglicht, ein Objekt auf eine Weise zu konstruieren, die dem Benutzer verboten ist. Ein Beispiel, das mir in den Sinn kommt, wäre ein Container, der einen Iterator (C ++) erstellt:
Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
// and Container is a friend of Iterator.
friend
wenn dies nicht gerechtfertigt ist - und es gibt viele Fälle, in denen dies eine schlechte Idee ist. Leider wurde die Nachricht zu gut aufgenommen, und viele Entwickler lernen die Sprache nie gut genug, um zu verstehen, dass die gelegentliche Verwendung friend
nicht nur akzeptabel, sondern auch vorzuziehen ist . Ihr Beispiel war genau so ein Fall. Absichtliche starke Kopplung ist kein Verbrechen, sondern eine Entwurfsentscheidung.
Jeder steckt in der Singleton-Sache fest, wow.
Andere Dinge:
Dies kann für einen Konstruktor sehr nützlich sein, der allgemeinen Code enthält. Private Konstruktoren können von anderen Konstruktoren mit 'this (...);' aufgerufen werden. Notation. Indem Sie den allgemeinen Initialisierungscode in einem privaten (oder geschützten) Konstruktor erstellen, machen Sie auch explizit klar, dass er nur während der Erstellung aufgerufen wird, was nicht der Fall ist, wenn es sich lediglich um eine Methode handelt:
public class Point {
public Point() {
this(0,0); // call common constructor
}
private Point(int x,int y) {
m_x = x; m_y = y;
}
};
In einigen Fällen möchten Sie möglicherweise keinen öffentlichen Konstruktor verwenden. Zum Beispiel, wenn Sie eine Singleton-Klasse möchten.
Wenn Sie eine Assembly schreiben, die von Drittanbietern verwendet wird, gibt es möglicherweise eine Reihe interner Klassen, die nur von Ihrer Assembly erstellt und nicht von Benutzern Ihrer Assembly instanziiert werden sollen.
Dadurch wird sichergestellt, dass Sie (die Klasse mit privatem Konstruktor) steuern, wie der Konstruktor aufgerufen wird.
Ein Beispiel: Eine statische Factory-Methode für die Klasse kann Objekte zurückgeben, wenn die Factory-Methode sie zuweist (z. B. eine Singleton-Factory).
Wir können auch einen privaten Konstruktor haben, um die Erstellung des Objekts nur durch eine bestimmte Klasse durchzuführen (aus Sicherheitsgründen).
C ++ Beispiel:
class ClientClass;
class SecureClass
{
private:
SecureClass(); // Constructor is private.
friend class ClientClass; // All methods in
//ClientClass have access to private
// & protected methods of SecureClass.
};
class ClientClass
{
public:
ClientClass();
SecureClass* CreateSecureClass()
{
return (new SecureClass()); // we can access
// constructor of
// SecureClass as
// ClientClass is friend
// of SecureClass.
}
};
Hinweis: Hinweis: Nur ClientClass (da es mit SecureClass befreundet ist) kann den Konstruktor von SecureClass aufrufen.
Hier sind einige der Verwendungen von privaten Konstruktoren:
Wenn Sie nicht möchten, dass Benutzer Instanzen dieser Klasse erstellen oder eine Klasse erstellen, die diese Klasse erbt, wie z. B. die java.lang.math
gesamte Funktion in diesem Paket static
. Alle Funktionen können aufgerufen werden, ohne eine Instanz von zu erstellen. Daher math
wird der Konstruktor als statisch angekündigt .
Ich habe eine Frage von Ihnen gesehen, die sich mit demselben Thema befasst.
Wenn Sie den anderen nicht erlauben möchten, Instanzen zu erstellen, halten Sie den Konstruktor in einem begrenzten Bereich. Die praktische Anwendung (ein Beispiel) ist das Singleton-Muster.
Sie sollten den Konstruktor nicht privat machen. Zeitraum. Machen Sie es geschützt, damit Sie die Klasse bei Bedarf erweitern können.
Edit: Ich stehe dazu, egal wie viele Downvotes du darauf wirfst. Sie schneiden das Potenzial für zukünftige Entwicklungen des Codes ab. Wenn andere Benutzer oder Programmierer wirklich entschlossen sind, die Klasse zu erweitern, ändern sie einfach den Konstruktor in Quell- oder Bytecode-Schutz. Sie werden nichts anderes erreicht haben, als ihr Leben ein wenig schwieriger zu machen. Fügen Sie eine Warnung in die Kommentare Ihres Konstruktors ein und belassen Sie sie dabei.
Wenn es sich um eine Utility-Klasse handelt, besteht die einfachere, korrektere und elegantere Lösung darin, die gesamte Klasse als "statisch endgültig" zu markieren, um eine Erweiterung zu verhindern. Es nützt nichts, den Konstruktor nur als privat zu markieren. Ein wirklich entschlossener Benutzer kann immer Reflexion verwenden, um den Konstruktor zu erhalten.
protected
Konstruktors besteht darin, die Verwendung statischer Factory-Methoden zu erzwingen, mit denen Sie die Instanziierung einschränken oder teure Ressourcen (DB-Verbindungen, native Ressourcen) bündeln und wiederverwenden können.Der Konstruktor ist für einen bestimmten Zweck privat, z. B. wenn Sie Singleton implementieren oder die Anzahl der Objekte einer Klasse begrenzen müssen. Zum Beispiel müssen wir in der Singleton-Implementierung den Konstruktor privat machen
#include<iostream>
using namespace std;
class singletonClass
{
static int i;
static singletonClass* instance;
public:
static singletonClass* createInstance()
{
if(i==0)
{
instance =new singletonClass;
i=1;
}
return instance;
}
void test()
{
cout<<"successfully created instance";
}
};
int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{
singletonClass *temp=singletonClass::createInstance();//////return instance!!!
temp->test();
}
Wenn Sie die Objekterstellung auf 10 beschränken möchten, verwenden Sie Folgendes
#include<iostream>
using namespace std;
class singletonClass
{
static int i;
static singletonClass* instance;
public:
static singletonClass* createInstance()
{
if(i<10)
{
instance =new singletonClass;
i++;
cout<<"created";
}
return instance;
}
};
int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{
singletonClass *temp=singletonClass::createInstance();//return an instance
singletonClass *temp1=singletonClass::createInstance();///return another instance
}
Vielen Dank
Sie können mehr als einen Konstruktor haben. C ++ bietet einen Standardkonstruktor und einen Standardkopierkonstruktor, wenn Sie keinen explizit angeben. Angenommen, Sie haben eine Klasse, die nur mit einem parametrisierten Konstruktor erstellt werden kann. Vielleicht hat es Variablen initialisiert. Wenn ein Benutzer diese Klasse dann ohne diesen Konstruktor verwendet, kann dies zu Problemen führen. Eine gute allgemeine Regel: Wenn die Standardimplementierung nicht gültig ist, machen Sie sowohl den Standardkonstruktor als auch den Kopierkonstruktor privat und geben Sie keine Implementierung an:
class C
{
public:
C(int x);
private:
C();
C(const C &);
};
Verwenden Sie den Compiler, um zu verhindern, dass Benutzer das Objekt mit den ungültigen Standardkonstruktoren verwenden.
Wenn Sie aus Effective Java zitieren , können Sie eine Klasse mit privatem Konstruktor haben, um eine Dienstprogrammklasse zu haben, die Konstanten definiert (als statische Endfelder).
( BEARBEITEN: Gemäß dem Kommentar ist dies möglicherweise nur mit Java anwendbar. Ich weiß nicht, ob dieses Konstrukt in anderen OO-Sprachen anwendbar ist / benötigt wird (z. B. C ++).)
Ein Beispiel wie folgt:
public class Constants {
private Contants():
public static final int ADDRESS_UNIT = 32;
...
}
EDIT_1 : Auch hier gilt die folgende Erklärung in Java: (und unter Bezugnahme auf das Buch Effective Java )
Eine Instanziierung einer Utility-Klasse wie die folgende ist zwar nicht schädlich, hat jedoch keinen Zweck, da sie nicht für die Instanziierung ausgelegt ist.
Angenommen, es gibt keinen privaten Konstruktor für Klassenkonstanten. Ein Codeblock wie unten ist gültig, vermittelt jedoch nicht besser die Absicht des Benutzers der Constants-Klasse
unit = (this.length)/new Constants().ADDRESS_UNIT;
im Gegensatz zu Code wie
unit = (this.length)/Constants.ADDRESS_UNIT;
Ich denke auch, dass ein privater Konstrukteur die Absicht des Designers der Constants (sagen wir) Klasse besser vermittelt.
Java bietet einen standardmäßigen parameterlosen öffentlichen Konstruktor, wenn kein Konstruktor bereitgestellt wird und wenn Sie die Instanziierung verhindern möchten, wird ein privater Konstruktor benötigt.
Man kann eine statische Klasse der obersten Ebene nicht markieren und sogar eine letzte Klasse kann instanziiert werden.
Utility-Klassen können private Konstruktoren haben. Benutzer der Klassen sollten diese Klassen nicht instanziieren können:
public final class UtilityClass {
private UtilityClass() {}
public static utilityMethod1() {
...
}
}
Eine der wichtigsten Anwendungen ist die SingleTon-Klasse
class Person
{
private Person()
{
//Its private, Hense cannot be Instantiated
}
public static Person GetInstance()
{
//return new instance of Person
// In here I will be able to access private constructor
}
};
Es ist auch geeignet, wenn Ihre Klasse nur statische Methoden hat. dh niemand muss Ihre Klasse instanziieren
Es ist wirklich ein offensichtlicher Grund: Sie möchten ein Objekt erstellen, aber es ist nicht praktisch, dies (in Bezug auf die Schnittstelle) innerhalb des Konstruktors zu tun.
Das Factory
Beispiel ist ganz offensichtlich, lassen Sie mich die Named Constructor
Redewendung demonstrieren .
Angenommen, ich habe eine Klasse, Complex
die eine komplexe Zahl darstellen kann.
class Complex { public: Complex(double,double); .... };
Die Frage ist: Erwartet der Konstruktor den Real- und Imaginärteil oder erwartet er die Norm und den Winkel (Polarkoordinaten)?
Ich kann die Benutzeroberfläche ändern, um es einfacher zu machen:
class Complex
{
public:
static Complex Regular(double, double = 0.0f);
static Complex Polar(double, double = 0.0f);
private:
Complex(double, double);
}; // class Complex
Dies wird als Named Constructor
Redewendung bezeichnet: Die Klasse kann nur von Grund auf neu erstellt werden, indem explizit angegeben wird, welchen Konstruktor wir verwenden möchten.
Es ist ein Sonderfall vieler Bauweisen. Die Entwurfsmuster bieten eine gute Anzahl von Möglichkeiten , um Bauobjekt: Builder
, Factory
, Abstract Factory
, ... und ein privater Konstruktor wird sichergestellt , dass der Benutzer richtig eingeschränkt.
Neben den bekannteren Verwendungen…
So implementieren Sie das Methodenobjektmuster , das ich wie folgt zusammenfassen würde:
"Privater Konstruktor, öffentliche statische Methode"
"Objekt für die Implementierung, Funktion für die Schnittstelle"
Wenn Sie eine Funktion mithilfe eines Objekts implementieren möchten und das Objekt außerhalb einer einmaligen Berechnung (durch einen Methodenaufruf) nicht nützlich ist, verfügen Sie über ein Wegwerfobjekt . Sie können die Objekterstellung und den Methodenaufruf in einer statischen Methode kapseln, um dieses allgemeine Anti-Pattern zu verhindern:
z = new A(x,y).call();
… Durch einen (namengebundenen) Funktionsaufruf ersetzen:
z = A.f(x,y);
Der Anrufer muss niemals wissen oder sich darum kümmern, dass Sie ein Objekt intern verwenden, eine sauberere Benutzeroberfläche erhalten und verhindern, dass Müll vom Objekt herumhängt oder das Objekt falsch verwendet wird.
Zum Beispiel, wenn Sie eine Berechnung über Methoden zu brechen foo
, bar
und zork
zum Beispiel auf Aktien Zustand ohne viele Werte passieren zu müssen in und aus Funktionen, man könnte es wie folgt implementieren:
class A {
public static Z f(x, y) {
A a = new A(x, y);
a.foo();
a.bar();
return a.zork();
}
private A(X x, Y y) { /* ... */ };
}
Dieses Methodenobjektmuster finden Sie in Smalltalk Best Practice Patterns , Kent Beck, S. 34–37. Dort ist es der letzte Schritt eines Refactoring-Musters und endet mit:
- Ersetzen Sie die ursprüngliche Methode durch eine, die eine Instanz der neuen Klasse erstellt, die mit den Parametern und dem Empfänger der ursprünglichen Methode erstellt wurde, und rufen Sie "compute" auf.
Dies unterscheidet sich erheblich von den anderen Beispielen hier: Die Klasse ist instanziierbar (im Gegensatz zu einer Utility-Klasse), aber die Instanzen sind privat (im Gegensatz zu Factory-Methoden, einschließlich Singletons usw.) und können auf dem Stapel leben, da sie niemals entkommen.
Dieses Muster ist sehr nützlich bei Bottom-Up-OOP, bei dem Objekte verwendet werden, um die Implementierung auf niedriger Ebene zu vereinfachen, aber nicht unbedingt extern verfügbar gemacht werden, und steht im Gegensatz zu dem Top-Down-OOP, das häufig dargestellt wird und mit Schnittstellen auf hoher Ebene beginnt.
Manchmal ist es nützlich, wenn Sie steuern möchten, wie und wann (und wie viele) Instanzen eines Objekts erstellt werden.
Unter anderem in Mustern verwendet:
Singleton pattern
Builder pattern
Die Verwendung privater Konstruktoren könnte auch dazu dienen, die Lesbarkeit / Wartbarkeit angesichts des domänengesteuerten Designs zu verbessern. Aus "Microsoft .NET - Erstellen von Anwendungen für Unternehmen, 2. Ausgabe":
var request = new OrderRequest(1234);
Zitat: "Hier gibt es zwei Probleme. Erstens kann man beim Betrachten des Codes kaum erraten, was los ist. Eine Instanz von OrderRequest wird erstellt, aber warum und mit welchen Daten? Was ist 1234? Dies führt zum zweiten Problem: Sie verletzen die allgegenwärtige Sprache des begrenzten Kontexts. Die Sprache sagt wahrscheinlich Folgendes aus: Ein Kunde kann eine Bestellanfrage stellen und darf eine Kauf-ID angeben. Wenn dies der Fall ist, können Sie hier eine neue OrderRequest-Instanz abrufen : "
var request = OrderRequest.CreateForCustomer(1234);
wo
private OrderRequest() { ... }
public OrderRequest CreateForCustomer (int customerId)
{
var request = new OrderRequest();
...
return request;
}
Ich befürworte dies nicht für jede einzelne Klasse, aber für das obige DDD-Szenario halte ich es für absolut sinnvoll, eine direkte Erstellung eines neuen Objekts zu verhindern.