Risposte:
Alcuni motivi per cui potresti aver bisogno di un costruttore privato:
final
a livello di classe. Mettere costruttore privato è praticamente inutile per questo motivo.
final
. Questo è ciò che Sun ha fatto per la classe String e una porzione ragionevole dell'API che non deve essere estesa.
Fornendo un costruttore privato si impedisce la creazione di istanze di classe in qualsiasi luogo diverso da questa stessa classe. Esistono diversi casi d'uso per fornire tale costruttore.
A. Le tue istanze di classe vengono create in un static
metodo. Il static
metodo viene quindi dichiarato come public
.
class MyClass()
{
private:
MyClass() { }
public:
static MyClass * CreateInstance() { return new MyClass(); }
};
B. La tua classe è un singleton . Ciò significa che nel programma non esiste più di un'istanza della classe.
class MyClass()
{
private:
MyClass() { }
public:
MyClass & Instance()
{
static MyClass * aGlobalInst = new MyClass();
return *aGlobalInst;
}
};
C. (Si applica solo al prossimo standard C ++ 0x) Hai diversi costruttori. Alcuni sono dichiarati public
, altri private
. Per ridurre la dimensione del codice, i costruttori pubblici "chiamano" i costruttori privati che a loro volta svolgono tutto il lavoro. I vostri public
costruttori sono così chiamati costruttori delegati :
class MyClass
{
public:
MyClass() : MyClass(2010, 1, 1) { }
private:
MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};
D. Si desidera limitare la copia degli oggetti (ad esempio, a causa dell'utilizzo di una risorsa condivisa):
class MyClass
{
SharedResource * myResource;
private:
MyClass(const MyClass & theOriginal) { }
};
E. La tua classe è una classe di utilità . Ciò significa che contiene solo static
membri. In questo caso, nessuna istanza di oggetto deve mai essere creata nel programma.
Lasciare una "backdoor" che consente a un'altra classe / funzione di amico di costruire un oggetto in un modo proibito all'utente. Un esempio che viene in mente sarebbe un contenitore che costruisce un iteratore (C ++):
Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
// and Container is a friend of Iterator.
friend
dove non è garantito, e ci sono molti casi in cui è una cattiva idea. Purtroppo, il messaggio è stato ricevuto troppo bene e molti sviluppatori non imparano mai abbastanza bene la lingua per capire che l'uso occasionale di friend
non è solo accettabile, ma preferibile . Il tuo esempio è stato proprio un caso del genere. Il forte accoppiamento intenzionale non è un crimine, è una decisione di progettazione.
Tutti sono bloccati sulla cosa di Singleton, wow.
Altre cose:
Questo può essere molto utile per un costruttore che contiene codice comune; i costruttori privati possono essere chiamati da altri costruttori, usando "this (...);" notazione. Rendendo il codice di inizializzazione comune in un costruttore privato (o protetto), stai anche chiarendo esplicitamente che viene chiamato solo durante la costruzione, il che non è così se fosse semplicemente un metodo:
public class Point {
public Point() {
this(0,0); // call common constructor
}
private Point(int x,int y) {
m_x = x; m_y = y;
}
};
Ci sono alcuni casi in cui potresti non voler usare un costruttore pubblico; ad esempio se si desidera una classe singleton.
Se si sta scrivendo un assembly utilizzato da terze parti, potrebbero esserci un numero di classi interne che si desidera creare solo dall'assembly e non essere istanziate dagli utenti dell'assembly.
Ciò garantisce che tu (la classe con costruttore privato) controlli il modo in cui viene chiamato il contraente.
Un esempio: un metodo factory statico sulla classe potrebbe restituire oggetti quando il metodo factory sceglie di allocarli (come ad esempio una factory singleton).
Possiamo anche avere un costruttore privato, per enfore la creazione dell'oggetto solo da una classe specifica (per motivi di sicurezza).
Esempio C ++:
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.
}
};
Nota: Nota: solo ClientClass (poiché è amico di SecureClass) può chiamare il costruttore di SecureClass.
Ecco alcuni degli usi del costruttore privato:
quando non si desidera che gli utenti creino istanze di questa classe o creino una classe che eredita questa classe, come la java.lang.math
, tutte le funzioni in questo pacchetto sono static
, tutte le funzioni possono essere chiamate senza creare un'istanza di math
, quindi il costruttore è annunciato come statico .
Ho visto una tua domanda che affronta lo stesso problema.
Semplicemente se non vuoi consentire agli altri di creare istanze, allora mantieni il constuctor in un ambito limitato. L'applicazione pratica (un esempio) è il modello singleton.
Non dovresti rendere privato il costruttore. Periodo. Renderlo protetto, in modo da poter estendere la classe, se necessario.
Modifica: lo sto aspettando, non importa quanti voti negativi si danno a questo. Stai tagliando il potenziale per lo sviluppo futuro del codice. Se altri utenti o programmatori sono davvero determinati a estendere la classe, cambieranno semplicemente il costruttore in protetto in codice sorgente o bytecode. Non avrai realizzato nulla oltre a rendere la loro vita un po 'più dura. Includi un avviso nei commenti del costruttore e lascialo a quello.
Se si tratta di una classe di utilità, la soluzione più semplice, corretta e più elegante è contrassegnare l'intera classe "finale statico" per impedire l'estensione. Non serve a niente contrassegnare il costruttore come privato; un utente veramente determinato può sempre usare la riflessione per ottenere il costruttore.
protected
costruttore è quello di forzare l'uso di metodi statici di fabbrica, che consentono di limitare l'istanza o raggruppare e riutilizzare risorse costose (connessioni DB, risorse native).Il costruttore è privato per alcuni scopi, ad esempio quando è necessario implementare singleton o limitare il numero di oggetti di una classe. Ad esempio nell'implementazione di singleton dobbiamo rendere privato il costruttore
#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();
}
Ancora una volta, se si desidera limitare la creazione dell'oggetto a 10, utilizzare quanto segue
#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
}
Grazie
Puoi avere più di un costruttore. C ++ fornisce un costruttore predefinito e un costruttore di copie predefinito se non ne viene fornito uno esplicitamente. Supponiamo di avere una classe che può essere costruita solo usando un costruttore con parametri. Forse ha inizializzato le variabili. Se un utente utilizza quindi questa classe senza tale costruttore, può causare problemi senza fine. Una buona regola generale: se l'implementazione predefinita non è valida, rendi privati sia il costruttore predefinito sia il costruttore della copia e non fornire un'implementazione:
class C
{
public:
C(int x);
private:
C();
C(const C &);
};
Utilizzare il compilatore per impedire agli utenti di utilizzare l'oggetto con i costruttori predefiniti non validi.
Citando da Effective Java , è possibile avere una classe con costruttore privato per avere una classe di utilità che definisce le costanti (come campi finali statici).
( EDIT: secondo il commento questo è qualcosa che potrebbe essere applicabile solo con Java, non sono consapevole se questo costrutto è applicabile / necessario in altri linguaggi OO (diciamo C ++))
Un esempio come di seguito:
public class Constants {
private Contants():
public static final int ADDRESS_UNIT = 32;
...
}
EDIT_1 : Ancora una volta, la spiegazione di seguito è applicabile in Java: (e riferendosi al libro, Java efficace )
Un'istanza di classe di utilità come quella seguente, sebbene non dannosa, non serve a nulla poiché non è progettata per essere istanziata.
Ad esempio, supponiamo che non esista un costruttore privato per le costanti di classe. Un blocco di codice come di seguito è valido ma non trasmette meglio l'intenzione dell'utente della classe Costanti
unit = (this.length)/new Constants().ADDRESS_UNIT;
in contrasto con il codice come
unit = (this.length)/Constants.ADDRESS_UNIT;
Inoltre penso che un costruttore privato trasmetta meglio l'intenzione del progettista della classe Costanti (diciamo).
Java fornisce un costruttore pubblico senza parametri predefinito se non viene fornito alcun costruttore e se si intende impedire l'istanza, è necessario un costruttore privato.
Non è possibile contrassegnare una classe di livello superiore statica e persino una classe finale può essere istanziata.
Le classi di utilità potrebbero avere costruttori privati. Gli utenti delle classi non dovrebbero essere in grado di creare un'istanza di queste classi:
public final class UtilityClass {
private UtilityClass() {}
public static utilityMethod1() {
...
}
}
Uno degli usi importanti è nella classe SingleTon
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
}
};
È anche adatto, se la tua classe ha solo metodi statici. cioè nessuno ha bisogno di istanziare la tua classe
È davvero una ragione ovvia: vuoi costruire un oggetto, ma non è pratico farlo (in termini di interfaccia) all'interno del costruttore.
L' Factory
esempio è abbastanza ovvio, lasciatemi dimostrare il Named Constructor
linguaggio.
Supponiamo che io abbia una classe Complex
che può rappresentare un numero complesso.
class Complex { public: Complex(double,double); .... };
La domanda è: il costruttore si aspetta le parti reali e immaginarie o si aspetta la norma e l'angolo (coordinate polari)?
Posso cambiare l'interfaccia per renderlo più semplice:
class Complex
{
public:
static Complex Regular(double, double = 0.0f);
static Complex Polar(double, double = 0.0f);
private:
Complex(double, double);
}; // class Complex
Questo è chiamato Named Constructor
idioma: la classe può essere costruita da zero affermando esplicitamente quale costruttore vogliamo usare.
È un caso speciale di molti metodi di costruzione. I Design Patterns forniscono un buon numero di modi per costruire oggetti: Builder
, Factory
, Abstract Factory
, ... e un costruttore privato farà in modo che l'utente sia correttamente vincolato.
Oltre agli usi più noti ...
Per implementare il modello Object Method , che riassumerei come:
"Costruttore privato, metodo statico pubblico"
"Oggetto per implementazione, funzione per interfaccia"
Se si desidera implementare una funzione utilizzando un oggetto e l'oggetto non è utile al di fuori di eseguire un calcolo una tantum (mediante una chiamata di metodo), allora si dispone di un oggetto Throwaway . È possibile incapsulare la creazione dell'oggetto e la chiamata del metodo in un metodo statico, impedendo questo modello comune:
z = new A(x,y).call();
... sostituendolo con una chiamata di funzione (spazio dei nomi):
z = A.f(x,y);
Il chiamante non ha mai bisogno di sapere o preoccuparsi del fatto che stai usando un oggetto internamente, producendo un'interfaccia più pulita e prevenendo la spazzatura dall'oggetto in giro o un uso errato dell'oggetto.
Ad esempio, se si vuole rompere un calcolo attraverso metodi foo
, bar
e zork
, ad esempio, allo stato condivisione senza dover passare molti valori dentro e fuori di funzioni, è possibile implementare nel modo seguente:
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) { /* ... */ };
}
Questo modello di oggetto metodo è riportato in Smalltalk Best Practice Patterns , Kent Beck, pagine 34–37, dove è l'ultimo passaggio di un modello di refactoring, che termina:
- Sostituisci il metodo originale con uno che crea un'istanza della nuova classe, costruita con i parametri e il ricevitore del metodo originale, e invoca il "calcolo".
Ciò differisce in modo significativo dagli altri esempi qui: la classe è istantanea (a differenza di una classe di utilità), ma le istanze sono private (a differenza dei metodi di fabbrica, inclusi i singleton ecc.) E possono vivere nello stack, poiché non scappano mai.
Questo modello è molto utile in OOP dal basso verso l'alto, in cui gli oggetti vengono utilizzati per semplificare l'implementazione di basso livello, ma non sono necessariamente esposti esternamente e contrasta con l'OOP top-down che viene spesso presentato e inizia con interfacce di alto livello.
A volte è utile se si desidera controllare come e quando (e quante) istanze di un oggetto vengono create.
Tra gli altri, utilizzato nei modelli:
Singleton pattern
Builder pattern
L'uso di costruttori privati potrebbe anche aumentare la leggibilità / manutenibilità di fronte alla progettazione guidata dal dominio. Da "Microsoft .NET - Architecing Applications for the Enterprise, 2nd Edition":
var request = new OrderRequest(1234);
Citazione, "Ci sono due problemi qui. Innanzitutto, quando si guarda il codice, difficilmente si può indovinare cosa sta succedendo. Viene creata un'istanza di OrderRequest, ma perché e utilizzare quali dati? Che cosa è 1234? Questo porta al secondo problema: stai violando la lingua onnipresente del contesto limitato. La lingua probabilmente dice qualcosa del genere: un cliente può inviare una richiesta d'ordine e può specificare un ID di acquisto. In tal caso, ecco un modo migliore per ottenere una nuova istanza OrderRequest :"
var request = OrderRequest.CreateForCustomer(1234);
dove
private OrderRequest() { ... }
public OrderRequest CreateForCustomer (int customerId)
{
var request = new OrderRequest();
...
return request;
}
Non lo sto sostenendo per ogni singola classe, ma per lo scenario DDD di cui sopra penso che abbia perfettamente senso prevenire una creazione diretta di un nuovo oggetto.