Що таке статичні заводські методи?


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 також слід зменшити загальні З'єднання під час повернення з’єднання назад?
рухомий камінь

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

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

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
Зауважте, що статичний заводський метод не є тим самим, як заводський метод з шаблонів дизайну [Gamma95, с. 107]. Статичний заводський метод, описаний у цьому пункті, не має прямого еквівалента в шаблонах дизайну.
Josh Sunshine

Для мене в цій відповіді виграш - це посилання на об'єкти, що об'єднуються. Це ТОЧНО, як я використовую заводський метод. Фабричні методи надаються для управління життєвим циклом об'єкта: "create" або витягує об'єкт з пулу, або створює новий екземпляр, якщо пул порожній, "знищити" повертає його в пул для подальшого повторного використання.
MutantXenu

2
Ви дійсно повинні використовувати "статичний шаблон фабричного методу", де б ви не говорили "заводський шаблон методу" в цій публікації, ви використовуєте неправильний термін. Також ви посилаєтесь на статтю у Вікіпедії за іншим малюнком, ніж шаблон, про який ви говорите. Шаблон заводського методу, ймовірно, мав би бути названий шаблоном "Фабричний інтерфейс", оскільки він передбачає використання декількох заводських об'єктів, які реалізують заводський інтерфейс, щоб увімкнути єдиний алгоритм для отримання, а також працювати з екземплярами інтерфейсу, приймаючи заводський об'єкт який може створити конкретний бажаний підклас.
Теодор Мердок

8
Я згоден з @TheodoreMurdock. Ви вживаєте неправильний термін. Те, про що ви говорите, - це "Статичний заводський метод" Джошуа Блоха, але ви використовуєте термін "Заводський метод шаблону" GoF. Будь ласка, відредагуйте його, щоб інші люди не зрозуміли неправильно.
смарагдовий

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

171

ПРИМІТКА! « Статичний метод фабрики є НЕ такий же , як фабричний метод шаблону» (с) Effective Java, Джошуа Блох.

Фабричний метод: "Визначте інтерфейс для створення об'єкта, але дозвольте класам, які реалізують інтерфейс, вирішувати, який клас інстанціювати. Фабричний метод дозволяє відкладати екземпляри класу для підкласів" (c) GoF.

"Статичний заводський метод - це просто статичний метод, який повертає екземпляр класу." (c) Ефективна Java, Джошуа Блох. Зазвичай цей метод знаходиться всередині певного класу.

Різниця:

Ключова ідея статичного заводського методу - отримати контроль над створенням об'єкта та делегувати його від конструктора до статичного методу. Рішення об’єкта, який потрібно створити, схоже на «Фабрика абстрактних», прийняту поза методом (у звичайному випадку, але не завжди). Хоча ключовою (!) Ідеєю Factory Method є делегування рішення, який екземпляр класу створити всередині Factory Method. Наприклад, класична реалізація Singleton - це особливий випадок статичного фабричного методу. Приклад часто використовуваних статичних заводських методів:

  • valueOf
  • getInstance
  • 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?
Ессей

@Baxtex: Так. Кожен зателефонує приватному конструктору. Можливо, саме те саме: private Foo(boolean withBar){/*..*/} public static Foo createWithBar(){return new Foo(true);} public static Foo createWithoutBar(){return new Foo(false);}
Расмус Фабер

Я думаю, це не дуже «масштаб». Якщо у вас є три і більше параметрів, як ви могли використовувати цю ідею і створити приємну назву методу?
Дерік

@Dherik: Тоді ви, мабуть, замість цього використовуєте шаблон конструктора: новий FooBuilder (). WithBar (). WithoutFoo (). WithBaz (). Build ();
Расмус Фабер

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

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


18

Це все зводиться до ремонту. Найкращий спосіб зробити це кожен раз, коли ви використовуєтеnew ключове слово для створення об'єкта, ви з'єднуєте код, який ви пишете для реалізації.

Заводський зразок дозволяє вам відокремити спосіб створення об’єкта від того, що ви робите з об'єктом. Коли ви створюєте всі свої об'єкти за допомогою конструкторів, ви, по суті, жорстко з'єднуєте код, який використовує об'єкт для цієї реалізації. Код, який використовує ваш об’єкт, "залежить від" цього об'єкта. Це може здатися не великою справою на поверхні, але коли об'єкт змінюється (думайте про зміну підпису конструктора або підкласифікацію об'єкта), вам доведеться повертатися назад і перевпорядкувати речі скрізь.

Сьогодні фабрики значною мірою відсторонені на користь використання залежної інжекції, оскільки їм потрібно багато кодового коду, який, як виявляється, трохи важко підтримувати. Інжекція залежностей в основному еквівалентна фабрикам, але дозволяє вказати, як ваші об'єкти декларативно з'єднуються між собою (за допомогою конфігурації чи анотацій).


3
Ви кажете, що фабрикам потрібна допомога? ;)
Джон Фаррелл

4
Ха-ха! У цей день і вік я думаю, що ми всі могли використати порятунок ... :)
перемикання

11

Якщо конструктор класу приватний, ви не можете створити об’єкт для класу поза ним.

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

Ми не можемо створити об'єкт для вище класу поза ним. Таким чином, ви не можете отримати доступ до x, y поза межами класу. Тоді в чому користь цього класу?
Ось відповідь: метод ФАБРИКИ .
Додайте нижченаведений метод у вищевказаний клас

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

Тож тепер ви можете створити об’єкт для цього класу поза його межами. Як і спосіб ...

Test t = Test.getObject();

Отже, статичний метод, який повертає об'єкт класу шляхом виконання його приватного конструктора, називається методом FACTORY
.


1
Чому ви називаєте це "рішенням"? Оголошення приватного конструктора не є "проблемою", це модель дизайну.
Ojonugwa Jude Ochalifu

1
Оновлено. Все одно дякую
Сантош

Привіт @Santhosh Мене хвилює: Чому ти не дозволиш приватному конструктору публічним? Якщо ви не хочете створювати підклас, для мене це добре, але якщо я не збираюся робити приватний конструктор, чи маємо ми користь від Static Factory Methodпублічного конструктора?
Шень

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 кожен раз, якщо ваша умова не буде задоволена.

Ще один приклад, взятий з Ефективної 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.
Josh Sunshine

4

Однією з переваг, що випливає з фабрики Static, є те, що API може повертати об'єкти, не роблячи їх класи загальнодоступними. Це призводить до дуже компактного API. У Java це досягається класом Collections, який приховує близько 32 класів, що робить API колекції дуже компактним.


2

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

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

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

Це захищає решту програми від змін, які ви можете внести на завод, що і є метою.

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


2

Однією з переваг статичних заводських методів з приватним конструктором (створення об'єкта повинно бути обмежено для зовнішніх класів, щоб забезпечити, щоб екземпляри не створювалися зовні) - це те, що ви можете створювати класи, керовані екземплярами . І керовані екземплярами класи гарантують, що не існує двох рівних відмінних екземплярів ( a.equals (b), якщо і лише якщо a == b ) під час роботи вашої програми, це означає, що ви можете перевірити рівність об'єктів з оператором == замість методу рівних , згідно з Ефективним Java.

Здатність статичних заводських методів повертати один і той же об'єкт із повторних викликів дозволяє класам підтримувати суворий контроль над тим, які екземпляри існують у будь-який час. Кажуть, що класи, які роблять це, контролюються примірниками. Є кілька причин писати класи, керовані екземплярами. Контроль екземплярів дозволяє класу гарантувати, що він є однотонним (п. 3) або невмирущим (п. 4). Крім того, це дозволяє непорушному класу (пункт 15) гарантувати відсутність двох рівних екземплярів: a.equals (b), якщо і лише якщо a == b. Якщо клас дає цю гарантію, то його клієнти можуть використовувати оператор == замість методу рівних (Об'єкт), що може призвести до підвищення продуктивності. Види Enum (пункт 30) дають цю гарантію.

Від ефективної Java, Джошуа Блох (Пункт 1, сторінка 6)


1

статичний

Член оголосив ключовим словом "статичний".

заводські методи

Методи, що створюють та повертають нові об’єкти.

на Java

Мова програмування відповідає значенню "статичний", але не для визначення "заводської".


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

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

@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.