Когда использовать конструктор, а когда использовать метод getInstance () (статические фабричные методы)?

84
  1. Когда и как использовать конструктор

    Foo bar = new Foo();
    
  2. И когда и как использовать getInstance () (статические фабричные методы)

    Foo bar = Foo.getInstance();
    

В чем разница между этими двумя? Я всегда использовал конструктор, но когда лучше использовать getInstance()?

Zengr
источник
Вы сами пишете урок? Если нет, то как вы звоните, чтобы это обеспечить?
Крис Б.
Итак, реализация самого класса означает, что я реализую одноэлементный шаблон, верно?
zengr 02
Вы звоните getInstance() или пишете метод под названием getInstance()?
Крис Б.
1
Если речь идет о конструкторских и статических фабричных методах , предлагаю уточнить и изменить заголовок.
Паскаль Тивент 02
@zengr: что касается вашего update2, это могло быть потому, что вы не назвали свой статический метод в соответствии с соглашением, которое требует, чтобы он был назван Foo.newInstance(). Foo.getInstance()- это соглашение о получении одноэлементного экземпляра класса. Вам следует исправить свой пример и использовать Foo.newInstance()вместо него.
JRL

Ответы:

97

Кажется, что все сосредоточены на синглтонах, в то время как я думаю, что на самом деле вопрос заключается в конструкторских и статических фабричных методах .

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

Правило 1. Рассмотрите статические фабричные методы вместо конструкторов

Обычный способ для класса позволить клиенту получить экземпляр самого себя - предоставить общедоступный конструктор. Есть еще один метод, который должен быть частью набора инструментов каждого программиста. Класс может предоставить общедоступный статический фабричный метод , который является просто статическим методом, возвращающим экземпляр класса. Вот простой пример из Boolean(упакованный примитивный класс для примитивного типа boolean). Этот метод переводит логическое примитивное значение в Booleanссылку на объект:

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

Обратите внимание, что статический фабричный метод отличается от шаблона фабричного метода из Design Patterns [Gamma95, p. 107]. Статический фабричный метод, описанный в этом элементе, не имеет прямого эквивалента в шаблонах проектирования .

Класс может предоставлять своим клиентам статические фабричные методы вместо конструкторов или в дополнение к ним. Предоставление статического фабричного метода вместо открытого конструктора имеет как преимущества, так и недостатки.

Достоинства (цитата из книги):

  • Одно из преимуществ статических фабричных методов состоит в том, что, в отличие от конструкторов, у них есть имена.
  • Второе преимущество статических фабричных методов состоит в том, что, в отличие от конструкторов, от них не требуется создавать новый объект при каждом вызове.
  • Третье преимущество статических фабричных методов состоит в том, что, в отличие от конструкторов, они могут возвращать объект любого подтипа своего возвращаемого типа.
  • Четвертое преимущество статических фабричных методов состоит в том, что они уменьшают многословие при создании экземпляров параметризованных типов.

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

  • Основным недостатком предоставления только статических фабричных методов является то, что классы без общедоступных или защищенных конструкторов не могут быть разделены на подклассы.
  • Второй недостаток статических фабричных методов состоит в том, что их трудно отличить от других статических методов.
Паскаль Тивент
источник
8
Последнюю можно несколько смягчить. Я обычно использую статический внутренний класс под названием Factory и различные методы newInstance в нем, чтобы было понятнее при создании методов factory. Это спасло меня от охоты на время в прошлом. :)
Rich Schuler
@Qberticus - внутренний класс, предназначенный для фабричных методов, - хорошая идея. Я попробую, спасибо.
Дэйв О.
Конечно, конструкторы классов должны быть, по крайней мере, 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 02
6

Если вы можете использовать оба, то это звучит как плохо реализованный шаблон singleton .

Используйте второй вариант, если вы намереваетесь иметь только один экземпляр класса в вашей системе, и тогда сделайте конструктор закрытым.

Используйте первый, чтобы позволить построить несколько объектов класса.

НО не давайте вашему классу обе возможности.

Не допускайте чрезмерного использования синглтонов, используйте их только в том случае, если в системе действительно должен существовать только один экземпляр, иначе вы ограничите возможности повторного использования вашего класса в других проектах. Звучит интересно иметь возможность вызывать getInstance из любого места в вашем проекте, но из этого неясно, кто на самом деле владеет этим экземпляром: никто и / или все. Если в проекте много синглтонов, можно поспорить, что система плохо спроектирована (обычно). Синглтоны следует использовать с осторожностью, применимы те же советы, что и для глобальных переменных.

Jdehaan
источник
Обновил
4
------> «Будьте осторожны, чтобы не
злоупотреблять одиночными играми
1

Один случай, когда я всегда предпочитаю статическую фабрику обычному конструктору, - это когда я знаю, что создание объекта будет медленным. Я выполняю простую инициализацию конструктора, но если мне нужно создать что-то тяжелое, я использую статический метод и документирую его поведение.

Гуу
источник
0

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

Если вам действительно нужно использовать синглтон, убедитесь, что его область действия очень узкая, т.е. разумно ограничьте количество других объектов в вашей системе, которые о нем знают.

rupjones
источник