Коли використовувати конструктор, а коли метод getInstance () (статичні заводські методи)?


84
  1. Коли і як ми повинні використовувати конструктор

    Foo bar = new Foo();
    
  2. І коли і як ми повинні використовувати getInstance () (статичні заводські методи)

    Foo bar = Foo.getInstance();
    

Яка різниця між цими двома? Я завжди використовував конструктор, але коли я повинен використовувати його getInstance()?


Ви самі пишете клас? Якщо ні, то що ви телефонуєте, що це забезпечує?
Кріс Б.

отже, реалізація самого класу означає, що я реалізую одиночний шаблон, так?
zengr

Ви телефонуєте getInstance() , чи пишете метод, що називається getInstance()?
Кріс Б.

1
Якщо питання стосується конструктора проти статичних заводських методів , я пропоную уточнити та змінити заголовок.
Pascal Thivent

@zengr: стосовно вашого оновлення2 це могло бути через те, що ви не назвали свій статичний метод згідно з конвенцією, яка диктує, що його слід іменувати Foo.newInstance(). Foo.getInstance()є домовленістю для отримання екземпляра-сингтона класу. Вам слід виправити свій приклад і використовувати Foo.newInstance()замість нього.
JRL

Відповіді:


96

Здається, всі зосереджуються на одиночних файлах, тоді як я думаю, що питання насправді полягає в конструкторі проти статичних заводських методів .

Це на самому ділі Пункт 1: Розглянуть статичні фабричні методи замість конструкторів в Effective Java Джошуа Блох:

Пункт 1: Розглянемо статичні заводські методи замість конструкторів

Звичайний спосіб для класу дозволити клієнту отримати екземпляр самого себе - це надання публічного конструктора. Існує ще одна техніка, яка повинна бути частиною інструментарію кожного програміста. Клас може надати загальнодоступний статичний заводський метод , який є просто статичним методом, який повертає екземпляр класу. Ось простий приклад з Boolean(примітивний клас для примітивного типу boolean). Цей метод перекладає логічне примітивне значення у Booleanпосилання на об'єкт:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

Зверніть увагу, що статичний фабричний метод не є таким самим, як шаблон фабричного методу з Design Patterns [Gamma95, с. 107]. Статичний фабричний метод, описаний у цьому пункті, не має прямого еквівалента в Шаблонах проектування .

Клас може надавати своїм клієнтам статичні фабричні методи замість конструкторів або на додаток до них. Надання статичного заводського методу замість загальнодоступного конструктора має як переваги, так і недоліки.

Переваги (цитуючи книгу):

  • Однією з переваг статичних заводських методів є те, що вони, на відміну від конструкторів, мають імена.
  • Друга перевага статичних заводських методів полягає в тому, що, на відміну від конструкторів, їм не потрібно створювати новий об'єкт кожного разу, коли вони викликаються.
  • Третя перевага статичних заводських методів полягає в тому, що, на відміну від конструкторів, вони можуть повертати об'єкт будь-якого підтипу свого типу повернення.
  • Четвертою перевагою статичних заводських методів є те, що вони зменшують багатослівність створення екземплярів параметризованого типу.

Недоліки (все ще цитую книгу):

  • Основним недоліком забезпечення лише статичних заводських методів є те, що класи без загальнодоступних або захищених конструкторів не можуть бути підкласами.
  • Другим недоліком статичних заводських методів є те, що їх неможливо легко відрізнити від інших статичних методів.

8
Останній можна дещо пом’якшити. Я, як правило, маю статичний внутрішній клас, який називається Factory, і різні методи newInstance, щоб зробити його зрозумілішим при створенні заводських методів. У минулому це врятувало мене від полювання. :)
Rich Schuler

@Qberticus внутрішній клас, присвячений заводським методам, є приємною ідеєю. Я спробую, thx.
Дейв О.

Безумовно, конструктори класів повинні бути принаймні protectedдля того, щоб дозволити підкласифікацію, але чи отримує код клієнта якусь реальну перевагу від створення нового об'єкта та виклику його конструктора, ніж виклик статичного фабричного методу? Мені здається, що ланцюговий виклик конструктора є семантично методом екземпляра, тоді як послідовність "створити неіціалізований об'єкт і викликати його конструктор" семантично еквівалентна виклику статичного фабричного методу.
supercat

9

У вас є два питання: коли я повинен подзвонити в getInstance()метод, і коли я повинен створити один?

Якщо ви вирішуєте , можна назватиgetInstance() метод, це легко. Вам просто потрібно прочитати документацію до класу, щоб з’ясувати, коли вам слід її викликати. Наприклад, NumberFormatнадає конструктор і на getInstance()метод; getInstance()метод дасть вам локалізовані NumberFormat. З Calendarіншого боку, конструктор захищений. Вам потрібно зателефонувати, getInstance()щоб отримати його.

Якщо ви вирішуєте , слід створити в getInstance()метод, ви повинні вирішити , що ви намагаєтеся досягти. Або ви не хочете, щоб люди викликали ваш конструктор (ви створюєте синглтон або фабрику ), або ви не проти (як це було NumberFormatвище, де вони ініціалізують деякі об’єкти для зручності абонента).


Довга історія? Не хвилюйтеся про створення getInstance()методів у власному коді. Якщо настане час, коли вони стануть у нагоді, ви дізнаєтесь. І взагалі, якщо ви можете викликати конструктор класу, ви, мабуть, повинні це робити, навіть якщо клас надає getInstance()метод.


7

Використання методів getInstance:

  • Якщо ви хочете контролювати / обмежувати будівництво, наприклад, Singleton
  • реалізувати заводський шаблон, наприклад DriverManager.getConnection
  • Коли ви хочете вказати кращу назву щодо того, як будується екземпляр (конструктори повинні мати те саме ім'я, що і ім'я класу), перевірте заводські методи NumberFormat getCurrencyInstance , getIntegerInstance та інші як приклади цього.

Але більшу частину часу ваш об’єкт буде простим POJO, і використання публічних конструкторів є найбільш практичним і очевидним рішенням.

U1: getInstance з іншого класу

Щоб повернути екземпляр іншого класу:

public class FooFactory {
    public static Foo getInstance() {
        return new Foo();
    }
}

NumberFormat.getInstanceметоди роблять це, оскільки фактично повертають екземпляри DecimalFormat.

U2: Проблеми з одиночкою

Шаблон-одинар обмежує багато переваг об’єктно-орієнтованого програмування. Як правило, одиночні конструкції мають приватні конструктори, тому їх не можна розширювати. Оскільки ви будете отримувати до нього доступ за допомогою методу getInstance і не посилатись на жоден інтерфейс, ви не зможете поміняти його на іншу реалізацію.


у випадку заводського зразка інше ім'я методу, як 'createInstance' або 'buildInstance', набагато підходило б, але все-таки можливий випадок того, що може вимагати запитувач (це все одно звучить трохи неясно і як для мене домашнє завдання)
jdehaan

6

Якщо ви можете використовувати обидва, то це звучить як погано реалізований одиночний шаблон .

Використовуйте другий варіант, якщо ви маєте намір мати у вашій системі лише один екземпляр класу і зробити конструктор приватним тоді.

Використовуйте перший, щоб дозволити побудову кількох об’єктів класу.

АЛЕ не дайте своєму класу обох можливостей.

Будьте обережні, щоб не використовувати надто однотонні, використовуйте їх лише в тому випадку, якщо в системі дійсно існує лише один екземпляр, інакше ви обмежите можливості повторного використання вашого класу в інших проектах. Звучить цікаво мати можливість викликати getInstance звідусіль у вашому проекті, але це робить незрозумілим, хто насправді володіє цим екземпляром: ніхто та / або всі. Якщо у вас багато одиночних проектів, ви можете поспоритись, що система погано розроблена (зазвичай). Однотонним слід користуватися обережно, застосовуються ті самі поради, що й для загальних змінних.


Оновив питання, трохи
заплутав

4
------> "Будьте обережні, щоб не використовувати надмірно великі" <------
Барт ван Хейкелом

1

Один із випадків, коли я завжди віддаю перевагу статичній фабриці перед звичайним конструктором, це коли я знаю, що побудова об'єкта буде повільною. Я роблю просту ініціалізацію в конструкторі, але якщо мені потрібно створити щось важке, я скористаюся статичним методом і зафіксую поведінку.


0

Самотні - це зло. Проблеми, які я бачив навколо нього, не стосуються повторного використання або розширення системи (хоча я бачив, як це могло статися), тим більше, що я не можу порахувати, скільки разів я бачив незрозумілі помилки в системи, що виникають з одиночних.

Якщо вам потрібно використати синглтон, переконайтеся, що його область надзвичайно вузька, тобто розумно обмежте кількість інших об’єктів у вашій системі, які знають про це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.