Что такое статические фабричные методы?


261

Что такое метод "статической фабрики"?


Альтернативные статические фабричные методы используют внедрение зависимостей.
CMCDragonkai

1
@ThangPham Ответ Джейсона Оуэна ошибочен, поскольку подразумевает, что речь идет о шаблоне фабричного метода, который очень отличается от шаблона статического фабричного метода, о котором он действительно говорит. Таким образом, хотя он хорошо отвечает на фактический вопрос, я не думаю, что он мог бы быть принят в его текущем состоянии, потому что он привносит несвязанный паттерн и увеличивает и без того невероятно распространенную путаницу в отношении различий между двумя паттернами.
Теодор Мердок

@CMCDragonkai Я думаю, что внедрение зависимости и статическая фабрика различны. Статическая фабрика может потребоваться даже в случае внедрения зависимостей для создания экземпляров зависимостей, которые будут внедрены.
Каран Ханна

Ответы:


126

Мы избегаем предоставления прямого доступа к соединениям с базой данных, поскольку они требуют значительных ресурсов. Поэтому мы используем статический метод фабрики, getDbConnectionкоторый создает соединение, если мы находимся ниже предела. В противном случае он пытается предоставить «запасное» соединение, в случае неудачи с исключением, если оно отсутствует.

public class DbConnection{
   private static final int MAX_CONNS = 100;
   private static int totalConnections = 0;

   private static Set<DbConnection> availableConnections = new HashSet<DbConnection>();

   private DbConnection(){
     // ...
     totalConnections++;
   }

   public static DbConnection getDbConnection(){

     if(totalConnections < MAX_CONNS){
       return new DbConnection();

     }else if(availableConnections.size() > 0){
         DbConnection dbc = availableConnections.iterator().next();
         availableConnections.remove(dbc);
         return dbc;

     }else {
         throw new NoDbConnections();
     }
   }

   public static void returnDbConnection(DbConnection dbc){
     availableConnections.add(dbc);
     //...
   }
}

если я правильно понимаю, вы можете добавить availableConnections.add (db) в метод returnDbConnection (DbConnection db)?
Хайфэн Чжан

@haifzhan, он не предназначен для полноты, но все в порядке.
Мэтью Флэшен

@MatthewFlaschen также следует уменьшить значение totalConnections при возврате соединения?
перекати

@Sridhar, нет, это количество существующих соединений (отслеженных таким образом, что не создается больше MAX_CONNS), а не количество циркулирующих.
Мэтью Флэшен

@MatthewFlaschen ваш метод будет пойман sonarcube как ошибка-блокировщик, если DbConnection имеет значение Closeable.
Аван Биру,

477

Статический шаблон Фабричный метод является способ создания объекта Encapsulate. Без фабричного метода, вы бы просто вызвать класс в конструктор напрямую: Foo x = new Foo(). С помощью этой модели, вы бы вместо того, чтобы вызвать метод фабрики: Foo x = Foo.create(). Конструкторы помечены как приватные, поэтому их нельзя вызывать, кроме как изнутри класса, а метод фабрики помечается staticтак, что его можно вызывать без предварительного наличия объекта.

У этой схемы есть несколько преимуществ. Во-первых, фабрика может выбирать из множества подклассов (или разработчиков интерфейса) и возвращать это. Таким образом, вызывающая сторона может указать желаемое поведение через параметры без необходимости знать или понимать потенциально сложную иерархию классов.

Еще одно преимущество, как указывали Мэтью и Джеймс, - контроль доступа к ограниченному ресурсу, такому как соединения. Это способ реализации пулов многократно используемых объектов - вместо создания, использования и разрушения объекта, если строительство и уничтожение являются дорогостоящими процессами, может быть более целесообразно их построить один раз и утилизировать. Фабричный метод может вернуть существующий, неиспользуемый экземплярный объект, если он есть, или создать его, если число объектов ниже некоторого нижнего порога, или выдать исключение или вернуть, nullесли он выше верхнего порога.

Согласно статье в Википедии, множественные фабричные методы также допускают различные интерпретации похожих типов аргументов. Обычно конструктор имеет то же имя, что и класс, что означает, что вы можете иметь только один конструктор с данной сигнатурой . Фабрики не так ограничены, что означает, что вы можете иметь два разных метода, которые принимают одинаковые типы аргументов:

Coordinate c = Coordinate.createFromCartesian(double x, double y)

и

Coordinate c = Coordinate.createFromPolar(double distance, double angle)

Это также может быть использовано для улучшения читабельности, как отмечает Расмус.


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

Для меня ответом на этот вопрос является ссылка на объединяемые объекты. Это именно то, как я использую шаблон фабричного метода. Для управления жизненным циклом объекта предусмотрены фабричные методы: «create» либо извлекает объект из пула, либо создает новый экземпляр, если пул пуст, «destroy» возвращает его в пул для последующего повторного использования.
MutantXenu

2
Вы действительно должны использовать «статический шаблон фабричного метода», где бы вы ни говорили «шаблон фабричного метода» в этом посте, вы используете неправильный термин. Кроме того, вы ссылаетесь на статью в Википедии по другому шаблону, чем тот, о котором вы говорите. Шаблон фабричного метода, вероятно, следовало бы назвать шаблоном «фабричный интерфейс», поскольку он предполагает использование нескольких фабричных объектов, которые реализуют фабричный интерфейс, чтобы позволить одному алгоритму создавать, а также работать с экземплярами интерфейса, принимая фабричный объект. который может произвести определенный желаемый подкласс.
Теодор Мердок

8
Я согласен с @TheodoreMurdock. Вы используете неправильный термин. То, о чем вы говорите, это «Статический метод фабрики» Джошуа Блоха, но вы используете термин «Шаблон метода фабрики» GoF. Пожалуйста, отредактируйте его, чтобы другие люди не поняли его неправильно.
Emeraldhieu

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

171

НОТА! « Статический метод фабрики является НЕ такой же , как Фабричный метод шаблона» (с) Effective Java, Джошуа Блох.

Метод Factory: «Определите интерфейс для создания объекта, но пусть классы, которые реализуют интерфейс, решают, какой класс создавать. Метод Factory позволяет классу отложить создание экземпляров для подклассов» (c) GoF.

«Метод статической фабрики - это просто статический метод, который возвращает экземпляр класса». (с) Эффективная Ява, Джошуа Блох. Обычно этот метод находится внутри определенного класса.

Различия:

Основная идея метода статической фабрики - получить контроль над созданием объекта и передать его от конструктора статическому методу. Решение об объекте, который будет создан, похоже на абстрактную фабрику, сделанную вне метода (в общем случае, но не всегда). В то время как ключевая (!) Идея Factory Method - делегировать решение о том, какой экземпляр класса создать внутри Factory Method. Например, классическая реализация Singleton является частным случаем статического фабричного метода. Пример часто используемых статических фабричных методов:

  • значение
  • деЫпзЬапсе
  • newInstance

Можете ли вы сказать мне разницу между новыми A () и A.newInstance ()? а внутри A.newInstance что нам делать?
Шен

69

Читаемость может быть улучшена статическими фабричными методами:

сравнить

public class Foo{
  public Foo(boolean withBar){
    //...
  }
}

//...

// What exactly does this mean?
Foo foo = new Foo(true);
// You have to lookup the documentation to be sure.
// Even if you remember that the boolean has something to do with a Bar
// you might not remember whether it specified withBar or withoutBar.

в

public class Foo{
  public static Foo createWithBar(){
    //...
  }

  public static Foo createWithoutBar(){
    //...
  }
}

// ...

// This is much easier to read!
Foo foo = Foo.createWithBar();

Поэтому я попытался реализовать ваш пример, но я не уверен, как это работает. Или два метода createWithBar и createWithoutBar должны вызывать 2 частных конструктора внутри класса Foo?
Essej

@Baxtex: Да. Каждый вызовет приватный конструктор. Возможно, точно такой же: private Foo(boolean withBar){/*..*/} public static Foo createWithBar(){return new Foo(true);} public static Foo createWithoutBar(){return new Foo(false);}
Расмус Фабер,

Я думаю, что это не "масштаб" очень хорошо. Если у вас есть три или более параметров, как вы могли бы использовать эту идею и создать хорошее имя для метода?
Дерик

@Dherik: Тогда вам, вероятно, следует вместо этого использовать шаблон построителя: new FooBuilder (). WithBar () .lessFoo (). WithBaz (). Build ();
Расмус Фабер

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

с http://www.javapractices.com/topic/TopicAction.do?Id=21


18

Все сводится к ремонтопригодности. Лучший способ это сделать - всякий раз, когда вы используете newключевое слово для создания объекта, вы связываете код, который вы пишете, с реализацией.

Шаблон фабрики позволяет вам отделить способ создания объекта от того, что вы делаете с объектом. Когда вы создаете все свои объекты с использованием конструкторов, вы, по сути, жестко связываете код, который использует объект для этой реализации. Код, который использует ваш объект, «зависит» от этого объекта. На первый взгляд это может показаться не таким уж большим делом, но когда объект меняется (подумайте об изменении сигнатуры конструктора или создании подкласса объекта), вам придется вернуться назад и переписать вещи повсюду.

Сегодня фабрики в значительной степени отброшены в пользу использования Dependency Injection, потому что они требуют большого количества кода, который оказывается немного сложным в обслуживании. Внедрение зависимостей в основном эквивалентно фабрикам, но позволяет вам указать, как ваши объекты декларативно соединяются (через конфигурацию или аннотации).


3
Вы говорите, что фабрики нуждаются в финансовой помощи? ;)
Джон Фаррелл

4
Ха - ха! Я думаю, что в наши дни мы все могли бы использовать помощь ... :)
cwash

11

Если конструктор класса является закрытым, вы не можете создать объект для класса извне.

class Test{
 int x, y;
 private Test(){
  .......
  .......
  }
}

Мы не можем создать объект для вышеупомянутого класса извне. Таким образом, вы не можете получить доступ к x, y извне класса. Тогда какая польза от этого класса?
Вот метод Ответ: ФАБРИКА .
Добавьте метод ниже в классе выше

public static Test getObject(){
  return new Test();
}

Так что теперь вы можете создать объект для этого класса извне. Нравится, как ...

Test t = Test.getObject();

Следовательно, статический метод, который возвращает объект класса путем выполнения его частного конструктора, называется методом FACTORY
.


1
Почему вы называете это «решением»? Объявление частного конструктора - это не проблема, это шаблон проектирования.
Оджонугва Джуд Очалифу

1
Обновлено. Спасибо в любом случае
Сантош

Привет @Santhosh У меня есть беспокойство Почему ты не разрешаешь приватному конструктору быть публичным? Если вы не хотите создавать подкласс, это хорошо для меня, но если я не собираюсь делать приватный конструктор, есть ли у нас какое-то преимущество Static Factory Methodперед публичным конструктором?
Shen

9

Я думал, что добавлю немного света в этот пост на то, что я знаю. Мы широко использовали эту технику в нашем recent android project. Вместо этого creating objects using new operatorвы также можете использовать static methodдля создания экземпляра класса. Список кодов:

//instantiating a class using constructor
Vinoth vin = new Vinoth(); 

//instantiating the class using static method
Class Vinoth{
  private Vinoth(){
  }
  // factory method to instantiate the class
  public static Vinoth getInstance(){
    if(someCondition)
        return new Vinoth();
  }
}

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

Еще один пример взят из Effective Java .

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

Этот метод переводит логическое значение примитива в ссылку на логический объект. Boolean.valueOf(boolean)Метод показывает нам, он никогда не создает объект. Возможность static factory methodsвозвращать один и тот же объект из повторяющихся invocationsпозволяет классам поддерживать строгий контроль над тем, какие экземпляры существуют в любое время.

Static factory methodsв том, что, в отличие от них constructors, они могут возвращать objectлюбой subtypeиз их типа возврата. Одним из применений этой гибкости является то, что API может возвращать объекты, не делая их классы общедоступными. Сокрытие классов реализации таким способом приводит к очень компактному API.

Calendar.getInstance () является отличным примером вышесказанному, он создает в зависимости от локали а BuddhistCalendar, JapaneseImperialCalendarили по умолчанию один Georgian.

Другой пример, о котором я могу подумать Singleton pattern, это когда вы делаете свои конструкторы приватными, создаете собственный getInstanceметод, в котором вы убедитесь, что всегда доступен только один экземпляр.

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

4

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

Это полезно при работе с иерархиями связанных классов, хорошим примером этого будет инструментарий GUI. Вы можете просто жестко закодировать вызовы конструкторов для конкретных реализаций каждого виджета, но если вы когда-нибудь захотите поменять один инструментарий на другой, у вас будет много мест для изменения. Используя фабрику, вы уменьшаете объем кода, который вам нужно изменить.


Предполагая, что ваша фабрика возвращает тип интерфейса, а не конкретный класс, с которым вы имеете дело.
Билл Линч

1
Этот ответ касается шаблона проектирования фабричных методов, а не статических фабричных методов. Статические фабричные методы - это просто открытый статический метод, который возвращает экземпляр класса. См. Главу 2 «Эффективная Java» для более подробной информации.
Джош Саншайн

4

Одно из преимуществ, которое вытекает из статической фабрики, состоит в том, что этот API может возвращать объекты, не делая их классы общедоступными. Это приводит к очень компактному API. В Java это достигается с помощью класса Collections, который скрывает около 32 классов, что делает его API-интерфейс очень компактным.


2

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

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

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

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

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


2

Одно из преимуществ статических методов фабрики с закрытым конструктором (создание объектов должно быть ограничено для внешних классов, чтобы экземпляры не создавались извне) заключается в том, что вы можете создавать управляемые экземплярами классы. А управляемые экземплярами классы гарантируют, что не существует двух одинаковых различных экземпляров ( a.equals (b) тогда и только тогда, когда a == b ) во время работы вашей программы, что означает, что вы можете проверить равенство объектов с помощью оператора == вместо метода equals В соответствии с действующим Java.

Способность статических методов фабрики возвращать один и тот же объект из повторных вызовов позволяет классам в любой момент поддерживать строгий контроль над тем, какие экземпляры существуют. Классы, которые делают это, называются контролируемыми экземплярами. Есть несколько причин для написания управляемых экземпляром классов. Контроль экземпляров позволяет классу гарантировать, что он является одноэлементным (элемент 3) или нереализуемым (элемент 4). Кроме того, он позволяет неизменному классу (элемент 15) гарантировать, что не существует двух одинаковых экземпляров: a.equals (b) тогда и только тогда, когда a == b. Если класс дает такую ​​гарантию, его клиенты могут использовать оператор == вместо метода equals (Object), что может привести к повышению производительности. Типы enum (пункт 30) предоставляют эту гарантию.

От эффективной Явы, Джошуа Блох (Элемент 1, страница 6)


1

статический

Член объявлен с ключевым словом «статический».

заводские методы

Методы, которые создают и возвращают новые объекты.

на Яве

Язык программирования имеет отношение к значению «статический», но не к определению «фабричный».


Ответы @Victor не обязательно должны содержать аргументы. Фактов должно быть достаточно: если они противоречивы, они могут быть подтверждены аргументами или цитатами. Я не знаю есть что - то спорным здесь.
Маркиз Лорн

Я заявил, что, поскольку ответ был слишком коротким и не совсем понятным с первого, второго и третьего взгляда, в любом случае ФП должен переспросить. Неважно, я передумал и пытаюсь убрать понижение, но я не могу.
Виктор

@Victor Определите «слишком коротко». Иногда реальный ответ - просто «да», «нет», «ни», ни «оба». «Слишком короткий» является полностью субъективным. Я до сих пор не понимаю ваш оригинальный комментарий. Вам не нужны аргументы для поддержки определений. По определению. Я отредактировал свой ответ, так что теперь было бы возможно удалить понижающий голос.
Маркиз Лорн

Конечно, это субъективно ... все ответы на вопросы stackoverflow являются субъективными, на самом деле они связаны с уровнем знаний автора и желанием ответить. Я действительно не понимаю ваш ответ, возможно, был короткий.
Виктор

@Victor Мусор. Фактические вопросы не являются субъективными и не могут быть выведены из выводов из фактов или аксиом. «Слишком короткий», с другой стороны, субъективен, и я уже обращался к нему. Этот ответ по сути такой же, как этот , который вы не прокомментировали. Ваши комментарии остаются неясными.
Маркиз Лорн

1

Реализация Java содержит классы утилит java.util.Arrays и java.util.Collections. Оба они содержат статические фабричные методы , примеры и способы их использования:

Также у класса java.lang.String есть такие статические фабричные методы :

  • String.format(...), String.valueOf(..), String.copyValueOf(...)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.