Odpowiedzi:
Niektóre powody, dla których możesz potrzebować prywatnego konstruktora:
final
poziom klasy. Z tego powodu umieszczanie prywatnego konstruktora jest praktycznie bezużyteczne.
final
. To właśnie zrobił Sun dla klasy String i rozsądny fragment API, którego nie należy rozszerzać.
Dostarczając prywatnego konstruktora zapobiegasz tworzeniu instancji klas w innym miejscu niż ta sama klasa. Istnieje kilka przypadków użycia takich konstruktorów.
A. Instancje klasy są tworzone w static
metodzie. static
Metoda jest następnie uznany jako public
.
class MyClass()
{
private:
MyClass() { }
public:
static MyClass * CreateInstance() { return new MyClass(); }
};
B. Twoja klasa to singleton . Oznacza to, że w programie nie istnieje więcej niż jedna instancja Twojej klasy.
class MyClass()
{
private:
MyClass() { }
public:
MyClass & Instance()
{
static MyClass * aGlobalInst = new MyClass();
return *aGlobalInst;
}
};
C. (Dotyczy tylko nadchodzącego standardu C ++ 0x) Masz kilka konstruktorów. Niektóre z nich są zadeklarowane public
, inne private
. Aby zmniejszyć rozmiar kodu, konstruktorzy publiczni „wywołują” konstruktory prywatne, które z kolei wykonują całą pracę. Twoje public
konstruktory są więc nazywane konstruktorami delegującymi :
class MyClass
{
public:
MyClass() : MyClass(2010, 1, 1) { }
private:
MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};
D. Chcesz ograniczyć kopiowanie obiektów (na przykład z powodu korzystania z udostępnionego zasobu):
class MyClass
{
SharedResource * myResource;
private:
MyClass(const MyClass & theOriginal) { }
};
E. Twoja klasa jest klasą użytkową . Oznacza to, że zawiera tylko static
członków. W takim przypadku w programie nie wolno nigdy tworzyć instancji obiektu.
Pozostawić „tylne drzwi”, które pozwalają innej klasie / funkcji znajomego na skonstruowanie obiektu w sposób zabroniony użytkownikowi. Przykładem, który przychodzi na myśl, byłby kontener konstruujący iterator (C ++):
Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
// and Container is a friend of Iterator.
friend
tego, gdy nie jest to uzasadnione - i jest wiele przypadków, w których jest to zły pomysł. Niestety, wiadomość została zbyt dobrze przyjęta i wielu programistów nigdy nie uczy się języka na tyle dobrze, aby zrozumieć, że sporadyczne używanie friend
jest nie tylko dopuszczalne, ale i preferowane . Twój przykład był właśnie takim przypadkiem. Celowe silne połączenie nie jest przestępstwem, to decyzja projektowa.
Każdy utknął w sprawie Singletona, wow.
Inne rzeczy:
Może to być bardzo przydatne w przypadku konstruktora, który zawiera wspólny kod; konstruktory prywatne mogą być wywoływane przez inne konstruktory przy użyciu opcji „this (...);” notacja. Tworząc wspólny kod inicjujący w konstruktorze prywatnym (lub chronionym), wyraźnie dajesz również do zrozumienia, że jest on wywoływany tylko podczas konstrukcji, co nie jest tak, gdyby była po prostu metodą:
public class Point {
public Point() {
this(0,0); // call common constructor
}
private Point(int x,int y) {
m_x = x; m_y = y;
}
};
W niektórych przypadkach możesz nie chcieć używać konstruktora publicznego; na przykład, jeśli chcesz klasę singleton.
Jeśli piszesz zestaw używany przez strony trzecie, może istnieć kilka klas wewnętrznych, które chcesz utworzyć tylko przez swój zestaw, a nie mają być tworzone przez użytkowników zestawu.
Gwarantuje to, że Ty (klasa z prywatnym konstruktorem) kontrolujesz sposób wywoływania konstruktora.
Przykład: statyczna metoda fabryczna klasy może zwracać obiekty, gdy metoda fabryki zdecyduje się je przydzielić (na przykład pojedyncza fabryka).
Możemy też mieć prywatnego konstruktora, który umożliwi stworzenie obiektu tylko przez określoną klasę (ze względów bezpieczeństwa).
Przykład w 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.
}
};
Uwaga: Uwaga: tylko ClientClass (ponieważ jest przyjacielem SecureClass) może wywoływać Konstruktora SecureClass.
Oto niektóre zastosowania prywatnego konstruktora:
jeśli nie chcesz, aby użytkownicy tworzyli instancje tej klasy lub tworzyli klasę, która dziedziczy tę klasę, jak np. java.lang.math
, cała funkcja w tym pakiecie jest static
, wszystkie funkcje można wywołać bez tworzenia instancji math
, więc konstruktor jest ogłaszany jako statyczny .
Widziałem Twoje pytanie dotyczące tego samego problemu.
Po prostu, jeśli nie chcesz pozwalać innym na tworzenie instancji, trzymaj konstruktor w ograniczonym zakresie. Praktyczne zastosowanie (przykład) to wzorzec singleton.
Nie powinieneś ustawiać konstruktora jako prywatnego. Kropka. Zadbaj o ochronę, aby w razie potrzeby przedłużyć klasę.
Edycja: Stoję przy tym, bez względu na to, ile głosów przeciw temu rzucisz. Odcinasz potencjał przyszłego rozwoju kodu. Jeśli inni użytkownicy lub programiści są naprawdę zdeterminowani, aby rozszerzyć klasę, po prostu zmienią konstruktor na chroniony w kodzie źródłowym lub bajtowym. Nie osiągniesz nic poza tym, że ich życie będzie trochę trudniejsze. Dołącz ostrzeżenie w komentarzach konstruktora i tak to zostaw.
Jeśli jest to klasa użytkowa, prostszym, poprawniejszym i bardziej eleganckim rozwiązaniem jest oznaczenie całej klasy „statycznym końcem”, aby zapobiec rozszerzaniu. Samo oznaczenie konstruktora jako prywatnego nie przyniesie nic dobrego; naprawdę zdeterminowany użytkownik może zawsze użyć refleksji, aby uzyskać konstruktora.
protected
konstruktora jest wymuszenie użycia statycznych metod fabryki, które pozwalają na ograniczenie tworzenia instancji lub puli i ponownego wykorzystania kosztownych zasobów (połączenia z bazą danych, zasoby natywne).Konstruktor jest prywatny w niektórych celach, na przykład gdy trzeba zaimplementować singleton lub ograniczyć liczbę obiektów klasy. Na przykład w implementacji singletona musimy ustawić konstruktor jako prywatny
#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();
}
Ponownie, jeśli chcesz ograniczyć tworzenie obiektu do 10, użyj następującego
#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
}
Dzięki
Możesz mieć więcej niż jednego konstruktora. C ++ udostępnia domyślny konstruktor i domyślny konstruktor kopiujący, jeśli nie podasz go jawnie. Załóżmy, że masz klasę, którą można zbudować tylko przy użyciu sparametryzowanego konstruktora. Może zainicjował zmienne. Jeśli użytkownik użyje tej klasy bez tego konstruktora, może to spowodować niekończące się problemy. Dobra zasada ogólna: jeśli domyślna implementacja jest nieprawidłowa, ustaw konstruktor domyślny i kopiujący jako prywatne i nie podawaj implementacji:
class C
{
public:
C(int x);
private:
C();
C(const C &);
};
Użyj kompilatora, aby uniemożliwić użytkownikom używanie obiektu z domyślnymi konstruktorami, które są nieprawidłowe.
Cytat z Effective Java , możesz mieć klasę z prywatnym konstruktorem, aby mieć klasę narzędziową, która definiuje stałe (jako statyczne pola końcowe).
( EDYTUJ: zgodnie z komentarzem jest to coś, co może mieć zastosowanie tylko w Javie, nie wiem, czy ta konstrukcja ma zastosowanie / jest potrzebna w innych językach OO (powiedzmy C ++))
Przykład jak poniżej:
public class Constants {
private Contants():
public static final int ADDRESS_UNIT = 32;
...
}
EDIT_1 : Ponownie, poniższe wyjaśnienie ma zastosowanie w Javie: (i na podstawie książki Effective Java )
Instancja klasy użytkowej takiej jak ta poniżej, choć nie jest szkodliwa, nie służy żadnemu celowi, ponieważ nie została zaprojektowana do tworzenia instancji.
Na przykład załóżmy, że nie ma prywatnego konstruktora dla stałych klasy. Fragment kodu jak poniżej jest prawidłowy, ale nie lepiej oddaje intencji użytkownika klasy Constants
unit = (this.length)/new Constants().ADDRESS_UNIT;
w przeciwieństwie do kodu takiego jak
unit = (this.length)/Constants.ADDRESS_UNIT;
Myślę też, że prywatny konstruktor lepiej oddaje intencje projektanta klasy Constants (powiedzmy).
Java udostępnia domyślny konstruktor publiczny bez parametrów, jeśli nie jest dostarczony żaden konstruktor, a jeśli zamierzasz zapobiec tworzeniu instancji, potrzebny jest konstruktor prywatny.
Nie można oznaczyć jako statycznej klasy najwyższego poziomu, a nawet można utworzyć instancję klasy końcowej.
Klasy narzędziowe mogą mieć prywatnych konstruktorów. Użytkownicy klas nie powinni mieć możliwości tworzenia instancji tych klas:
public final class UtilityClass {
private UtilityClass() {}
public static utilityMethod1() {
...
}
}
Jednym z ważnych zastosowań jest klasa 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
}
};
Jest również odpowiedni, jeśli twoja klasa ma tylko metody statyczne. tj. nikt nie musi tworzyć instancji Twojej klasy
To naprawdę jeden oczywisty powód: chcesz zbudować obiekt, ale nie jest to praktyczne (jeśli chodzi o interfejs) w konstruktorze.
Factory
Przykładem jest dość oczywiste, pozwól mi zademonstrować Named Constructor
idiom.
Powiedzmy, że mam klasę, Complex
która może reprezentować liczbę zespoloną.
class Complex { public: Complex(double,double); .... };
Pytanie brzmi: czy konstruktor oczekuje części rzeczywistej i urojonej, czy też oczekuje normy i kąta (współrzędne biegunowe)?
Mogę zmienić interfejs, żeby było łatwiej:
class Complex
{
public:
static Complex Regular(double, double = 0.0f);
static Complex Polar(double, double = 0.0f);
private:
Complex(double, double);
}; // class Complex
Nazywa się to Named Constructor
idiomem: klasę można zbudować tylko od zera, jawnie podając, którego konstruktora chcemy użyć.
To szczególny przypadek wielu metod konstrukcyjnych. Wzorce projektowe zapewniają dużą liczbę sposobów budowania obiektu: Builder
, Factory
, Abstract Factory
, ... i prywatny konstruktor będzie upewnić się, że użytkownik jest odpowiednio ograniczany.
Oprócz bardziej znanych zastosowań…
Aby zaimplementować wzorzec Method Object , który podsumowałbym jako:
„Prywatny konstruktor, publiczna metoda statyczna”
„Obiekt do implementacji, funkcja interfejsu”
Jeśli chcesz zaimplementować funkcję za pomocą obiektu, a obiekt nie jest przydatny poza wykonaniem jednorazowych obliczeń (przez wywołanie metody), masz obiekt Throwaway . Możesz hermetyzować tworzenie obiektu i wywołanie metody w metodzie statycznej, zapobiegając temu typowemu anty-wzorcowi:
z = new A(x,y).call();
… Zastępując go wywołaniem funkcji (z przestrzenią nazw):
z = A.f(x,y);
Wzywający nigdy nie musi wiedzieć ani dbać o to, że używasz obiektu wewnętrznie, uzyskując bardziej przejrzysty interfejs i zapobiegając śmieciom z obiektu wiszącego w pobliżu lub nieprawidłowego użycia obiektu.
Na przykład, jeśli chcesz, aby rozbić obliczenia całej metody foo
, bar
i zork
, na przykład do stanu zakładowego bez konieczności przechodzenia wiele wartości i wyjść z funkcji, można go realizować w następujący sposób:
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) { /* ... */ };
}
Ten wzorzec Method Object jest podany w Smalltalk Best Practice Patterns , Kent Beck, strony 34–37, gdzie jest ostatnim krokiem wzorca refaktoryzacji, kończącym się:
- Zastąp oryginalną metodę taką, która tworzy wystąpienie nowej klasy, skonstruowaną z parametrami i odbiornikiem oryginalnej metody i wywołuje „compute”.
Różni się to znacznie od innych przykładów tutaj: klasa jest instancyjna (w przeciwieństwie do klasy użytkowej), ale instancje są prywatne (w przeciwieństwie do metod fabrycznych, w tym singletonów itp.) I mogą żyć na stosie, ponieważ nigdy nie uciekają.
Ten wzorzec jest bardzo przydatny w OOP od dołu do góry, gdzie obiekty są używane do uproszczenia implementacji niskiego poziomu, ale niekoniecznie są ujawniane zewnętrznie i kontrastuje z odgórnym OOP, które jest często prezentowane i zaczyna się od interfejsów wysokiego poziomu.
Czasami jest to przydatne, jeśli chcesz kontrolować, jak i kiedy (i ile) instancji obiektu jest tworzonych.
Między innymi stosowane we wzorach:
Singleton pattern
Builder pattern
Korzystanie z prywatnych konstruktorów może również zwiększyć czytelność / łatwość konserwacji w obliczu projektowania opartego na domenie. Z „Microsoft .NET - Aplikacje do projektowania architektury dla przedsiębiorstw, wydanie 2”:
var request = new OrderRequest(1234);
Cytuj: „Występują tu dwa problemy. Po pierwsze, patrząc na kod, trudno zgadnąć, co się dzieje. Tworzona jest instancja OrderRequest, ale dlaczego i przy użyciu jakich danych? Co to jest 1234? Prowadzi to do drugiego problemu: naruszasz wszechobecny język ograniczonego kontekstu. Język prawdopodobnie mówi mniej więcej tak: klient może złożyć żądanie zamówienia i może podać identyfikator zakupu. W takim przypadku jest lepszy sposób na uzyskanie nowej instancji OrderRequest : "
var request = OrderRequest.CreateForCustomer(1234);
gdzie
private OrderRequest() { ... }
public OrderRequest CreateForCustomer (int customerId)
{
var request = new OrderRequest();
...
return request;
}
Nie zalecam tego dla każdej klasy, ale dla powyższego scenariusza DDD myślę, że ma sens zapobieganie bezpośredniemu tworzeniu nowego obiektu.