Відповіді:
Деякі причини, коли вам може знадобитися приватний конструктор:
final
на рівні класу. Поставляти приватний конструктор з цієї причини майже марно.
final
. Це те, що Sun зробив для класу String, і розумний фрагмент API, який не слід розширювати.
Надаючи приватний конструктор, ви запобігаєте створенню екземплярів класу в будь-якому місці, крім цього самого класу. Існує кілька випадків використання для надання такого конструктора.
A. Ваші екземпляри класу створені static
методом. Потім static
метод оголошується як public
.
class MyClass()
{
private:
MyClass() { }
public:
static MyClass * CreateInstance() { return new MyClass(); }
};
B. Ваш клас - одинокий . Це означає, що в програмі існує не більше одного примірника вашого класу.
class MyClass()
{
private:
MyClass() { }
public:
MyClass & Instance()
{
static MyClass * aGlobalInst = new MyClass();
return *aGlobalInst;
}
};
C. (стосується лише майбутнього стандарту C ++ 0x) У вас є кілька конструкторів. Деякі з них задекларовані public
, інші private
. Для зменшення розміру коду, державні конструктори "викликають" приватних конструкторів, які, в свою чергу, виконують всю роботу. public
Таким чином, ваші конструктори називаються конструкторами, що делегують :
class MyClass
{
public:
MyClass() : MyClass(2010, 1, 1) { }
private:
MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};
D. Ви хочете обмежити копіювання об'єктів (наприклад, через використання спільного ресурсу):
class MyClass
{
SharedResource * myResource;
private:
MyClass(const MyClass & theOriginal) { }
};
E. Ваш клас - корисний клас . Це означає, що він містить лише static
членів. У цьому випадку жоден примірник об'єкта ніколи не повинен створюватися в програмі.
Залишити "задню двері", що дозволяє іншому другому класу / функції будувати об'єкт забороненим для користувача способом. Прикладом, який спадає на думку, може бути контейнер, що створює ітератор (C ++):
Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
// and Container is a friend of Iterator.
friend
там, де це не гарантовано, - і є маса випадків, коли це погана ідея. На жаль, повідомлення було занадто добре сприйняте, і багато розробників ніколи не вивчають мову достатньо добре, щоб зрозуміти, що епізодичне використання friend
не тільки прийнятне, але і бажане . Ваш приклад був саме таким випадком. Навмисна сильна зв'язок - це не злочин, це дизайнерське рішення.
Усі застрягли на річ Синглтон, ух.
Інші речі:
Це може бути дуже корисно для конструктора, який містить загальний код; приватні конструктори можуть викликати інші конструктори, використовуючи 'this (...);' позначення. Створюючи загальний код ініціалізації в приватному (або захищеному) конструкторі, ви також чітко даєте зрозуміти, що він викликається лише під час побудови, що не так, якби це був просто метод:
public class Point {
public Point() {
this(0,0); // call common constructor
}
private Point(int x,int y) {
m_x = x; m_y = y;
}
};
Є деякі випадки, коли ви, можливо, не хочете використовувати громадський конструктор; наприклад, якщо ви хочете однокласний клас.
Якщо ви пишете збірку, використовувану третьою стороною, може бути ряд внутрішніх класів, які ви хочете створити лише вашою асамблеєю, а не користуватися ними.
Це гарантує, що ви (клас із приватним конструктором) контролюєте, як викликається кондуктор.
Приклад: статичний заводський метод класу може повертати об'єкти, оскільки заводський метод вирішує виділити їх (наприклад, завод однотонних).
Ми також можемо мати приватний конструктор, щоб передбачити створення об'єкта лише певним класом (з міркувань безпеки).
Приклад 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.
}
};
Примітка. Примітка. Тільки ClientClass (оскільки він є товаришем SecureClass) може викликати Конструктор SecureClass.
Ось деякі з використання приватного конструктора:
коли ви не хочете, щоб користувачі створювали екземпляри цього класу або створювали клас, який успадковує цей клас, як, наприклад java.lang.math
, вся функція в цьому пакеті static
, всі функції можна викликати без створення екземпляра math
, тому конструктор оголошується як статичний .
Я побачив від вас запитання, що стосується тієї ж проблеми.
Просто якщо ви не хочете дозволити іншим створювати екземпляри, тоді тримайте кондуктор в обмеженому обсязі. Практичне застосування (Приклад) - це однотонний малюнок.
Не слід робити конструктор приватним. Період. Зробіть його захищеним, щоб ви могли розширити клас, якщо потрібно.
Редагувати: Я стою на цьому, незалежно від того, скільки голосів ви кинете на це. Ви скорочуєте потенціал для подальшого розвитку коду. Якщо інші користувачі або програмісти дійсно налаштовані на розширення класу, вони просто змінять конструктор на захищений у вихідному чи байт-коді. Ви нічого не зробите, крім того, щоб зробити їх життя трохи важчим. Включіть попередження до коментарів вашого конструктора та залиште це при цьому.
Якщо це корисний клас, більш простим, правильним та елегантнішим рішенням є позначення всього класу "статичним фіналом" для запобігання розширення. Немає користі лише позначити конструктор приватним; дійсно визначений користувач завжди може використовувати відображення для отримання конструктора.
protected
конструктора - змусити використовувати статичні заводські методи, які дозволяють обмежити інстанціювання або об'єднати та повторно використовувати дорогі ресурси (з'єднання БД, рідні ресурси).Конструктор приватний для певних цілей, наприклад, коли вам потрібно реалізувати одиночний або обмежити кількість об'єктів класу. Наприклад, в одиночній реалізації ми повинні зробити конструктор приватним
#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();
}
Знову ж таки, якщо ви хочете обмежити створення об’єкта до 10, то скористайтеся наступним
#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
}
Дякую
У вас може бути більше одного конструктора. C ++ надає конструктор за замовчуванням і конструктор копій за замовчуванням, якщо ви не надаєте його явно. Припустимо, у вас є клас, який можна побудувати лише за допомогою деякого параметризованого конструктора. Можливо, це ініціалізовані змінні. Якщо користувач використовує цей клас без цього конструктора, вони можуть не викликати проблем. Хороше загальне правило: Якщо реалізація за замовчуванням не є дійсною, зробіть як конструктор за замовчуванням, так і конструктор копій приватним і не надайте реалізацію:
class C
{
public:
C(int x);
private:
C();
C(const C &);
};
Використовуйте компілятор, щоб запобігти користувачам використовувати об'єкт із конструкторами за замовчуванням, які не є дійсними.
Цитуючи з Ефективної Java , ви можете мати клас з приватним конструктором, щоб мати клас утиліти, який визначає константи (як статичні кінцеві поля).
( EDIT: Відповідно до коментаря, це щось, що може бути застосоване лише для Java, я не знаю, чи ця конструкція застосовна / потрібна в інших мовах ОО (скажімо, C ++))
Приклад, як показано нижче:
public class Constants {
private Contants():
public static final int ADDRESS_UNIT = 32;
...
}
EDIT_1 : Знову нижче, пояснення застосовується в Java: (і посилається на книгу " Ефективна Java" )
Опис класу корисності, як наведений нижче, хоч і не є шкідливим, але не виконує жодних цілей, оскільки вони не розроблені для примірника.
Наприклад, скажіть, що немає приватного конструктора для констант класу. Код, як показано нижче, є дійсним, але не краще передає наміри користувача класу Константи
unit = (this.length)/new Constants().ADDRESS_UNIT;
на відміну від коду типу
unit = (this.length)/Constants.ADDRESS_UNIT;
Також я думаю, що приватний конструктор краще передає наміри конструктора класу Константи (скажімо).
Java надає загальнодоступний конструктор без параметрів за замовчуванням, якщо не передбачений конструктор, і якщо ваш намір полягає у запобіганні інстанції, тоді потрібен приватний конструктор.
Не можна відзначити статику класу вищого рівня, і навіть остаточний клас можна створити екземпляром.
Корисні класи можуть мати приватних конструкторів. Користувачі класів не повинні мати можливість ініціювати ці класи:
public final class UtilityClass {
private UtilityClass() {}
public static utilityMethod1() {
...
}
}
Одне з важливих напрямків використання в класі 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
}
};
Він також підходить, якщо у вашому класі є лише статичні методи. тобто нікому не потрібно призначати ваш клас
Це дійсно одна очевидна причина: ви хочете створити об’єкт, але це не практично (в інтерфейсі) робити це в конструкторі.
Factory
Приклад абсолютно очевидно, дозвольте мені продемонструвати Named Constructor
ідіоми.
Скажіть, у мене клас, Complex
який може представляти складне число.
class Complex { public: Complex(double,double); .... };
Питання полягає в тому: чи очікує конструктор реальні та уявні частини, чи очікує норму та кут (полярні координати)?
Я можу змінити інтерфейс, щоб полегшити його:
class Complex
{
public:
static Complex Regular(double, double = 0.0f);
static Complex Polar(double, double = 0.0f);
private:
Complex(double, double);
}; // class Complex
Це називається Named Constructor
ідіомою: клас можна побудувати лише з нуля, чітко вказавши, який конструктор ми хочемо використовувати.
Це особливий випадок багатьох методів будівництва. Шаблони дизайну забезпечують велику кількість способів об'єкта збірки: Builder
, Factory
, Abstract Factory
, ... і приватний конструктор гарантує , що користувач правильно обмежений.
Окрім більш відомих цілей використання…
Для реалізації шаблону " Об'єкт методу" , який я б узагальнив як:
"Приватний конструктор, публічний статичний метод"
"Об'єкт для реалізації, функція для інтерфейсу"
Якщо ви хочете реалізувати функцію за допомогою об'єкта, а об'єкт не корисний поза одноразовим обчисленням (за допомогою виклику методу), тоді у вас є об'єкт Throwaway . Ви можете інкапсулювати створення об'єкта та виклик методу статичним методом, запобігаючи цьому поширеному антидіаграму:
z = new A(x,y).call();
… Замінивши його на виклик функції (у просторі імен):
z = A.f(x,y);
Користувачеві ніколи не потрібно знати або піклуватися про те, що ви використовуєте об'єкт внутрішньо, створюючи більш чистий інтерфейс і не допускаючи сміття з предмета, що висить навколо, або неправильного використання об'єкта.
Наприклад, якщо ви хочете розбити обчислення по методах foo
, bar
і zork
, наприклад, поділити стан, не маючи передавати багато значень у функції та виходити з них, ви можете реалізувати його наступним чином:
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) { /* ... */ };
}
Цей метод "Об'єктний шаблон" наведений у " Шаблонах найкращих практик" Smalltalk , Кент Бек, сторінки 34–37, де це останній крок шаблону рефакторингу, який закінчується:
- Замініть оригінальний метод тим, який створює екземпляр нового класу, побудований з параметрами та приймачем вихідного методу, і викликає "обчислити".
Це суттєво відрізняється від інших тут прикладів: клас є миттєвим (на відміну від класу утиліти), але екземпляри є приватними (на відміну від заводських методів, включаючи одиночні кнопки тощо), і можуть жити на стеці, оскільки вони ніколи не виходять.
Ця закономірність дуже корисна в OOP знизу-вгору, де об’єкти використовуються для спрощення реалізації низького рівня, але не обов'язково піддаються зовнішньому виконанню і контрастують з OOP зверху вниз, який часто представлений і починається з інтерфейсів високого рівня.
Іноді корисно, якщо ви хочете контролювати, як і коли створюються (і скільки) екземпляри об’єкта.
Серед інших, що використовуються в візерунках:
Singleton pattern
Builder pattern
Використання приватних конструкторів також може означати підвищення читабельності / ремонтопридатності в умовах дизайну, керованого доменом. З "Microsoft .NET - програми архітектури для підприємства, 2-е видання":
var request = new OrderRequest(1234);
Цитата: "Тут є дві проблеми. По-перше, дивлячись на код, навряд чи можна здогадатися, що відбувається. Створюється екземпляр OrderRequest, але чому і за допомогою яких даних? Що таке 1234? Це призводить до другої проблеми: ви порушуєте всюдисущу мову обмеженого контексту. Мова, ймовірно, говорить приблизно так: клієнт може надіслати запит на замовлення і йому вдається вказати ідентифікатор покупки. Якщо це так, ось кращий спосіб отримати новий екземпляр OrderRequest : "
var request = OrderRequest.CreateForCustomer(1234);
де
private OrderRequest() { ... }
public OrderRequest CreateForCustomer (int customerId)
{
var request = new OrderRequest();
...
return request;
}
Я не виступаю за це для кожного класу, але для вищезазначеного сценарію DDD я думаю, що має сенс запобігти прямому створенню нового об’єкта.