Скрытие конструктора за статическим методом создания?

18

Недавно я обнаружил интересный способ создания нового экземпляра объекта в Google Guava и Проект Ломбок : скрыть конструктор за статическим создатель. Это означает, что вместо выполнения new HashBiMap() вы делаете HashBiMap.create() .

Мой вопрос - почему? Какое преимущество у вас скрывает конструктор? Для меня я не вижу абсолютно никакого преимущества в этом, и, похоже, он нарушает принципы создания базовых объектов. С самого начала вы создаете объект с new Object() , а не некоторым методом Object.createMe() . Это похоже на создание метода для создания метода.

Что вы получаете от этого?

    
задан TheLQ 04.10.2010 в 02:28
источник
  • Обратите внимание, что вы получите предупреждения с использованием нового HashBiMap (), где вы не будете использовать HashBiMap.create (). Это основная причина, по которой Гува использует этот шаблон, как я уже упоминал в своем ответе. –  ColinD 04.10.2010 в 04:53

9 ответов

26

Существует несколько причин, по которым вы можете предпочесть статический заводский метод вместо публичного конструктора. Вы можете прочитать пункт 1 в Эффективном Java, второе издание для более длительного обсуждения.

  1. Он позволяет типу возвращаемого объектом метода отличаться от типа класса, который содержит этот метод. Фактически возвращаемый тип может зависеть от параметров. Например, EnumSet.of(E) вернет другой тип, если тип emum имеет очень мало элементов vs, если тип перечисления содержит много элементов ( Edit: в данном конкретном случае, что повышает производительность для общего случая, когда перечисление не имеет много элементов)
  2. Он позволяет кэшировать. Например, Integer.valueOf(x) по умолчанию возвращает один и тот же экземпляр объекта, если он вызван несколько раз с тем же значением x , если x находится между -128 и 127.
  3. Он позволяет вам иметь именованные конструкторы (которые могут быть полезны, если вашему классу нужны многие конструкторы). См., Например, методы в java.util.concurrent.Executors .
  4. Это позволяет вам создать API, который концептуально прост, но на самом деле очень мощный. Например, статические методы в Collections скрывают многие типы. Вместо того, чтобы иметь класс Collections со многими статическими методами, они могли бы создать множество публичных классов, но это было бы труднее для кого-то нового для понимания или запоминания языка.
  5. Для типичных типов он может ограничить объем ввода, который вам нужно сделать. Например, вместо того, чтобы набирать List<String> strings = new ArrayList<String>() в Guava, вы можете сделать List<String> strings = Lists.newArrayList() (метод newArrayList - это общий метод, и тип типичного типа выведен).

Для HashBiMap , последняя причина наиболее вероятна.

    
ответ дан NamshubWriter 04.10.2010 в 04:52
источник
  • Отличное объяснение! Спасибо за помощь –  TheLQ 10.10.2010 в 16:39
  • Хм, похоже, это все очень незначительные и крайние преимущества. Поскольку статические создатели / фабрики летят перед ООП, безудержное использование этого шаблона не кажется оправданным. –  Scott Biggs 07.08.2015 в 20:29
  • @ScottBiggs Я не согласен с тем, что статические методы создания «летают перед лицом ООП». Обратите внимание, что все эти причины исходят из Effective Java, Second Edition, который является окончательным руководством по лучшей практике для Java, и все примеры, которые я дал, принадлежат JDK. –  NamshubWriter 07.08.2015 в 20:34
  • Не стесняйтесь. Даже темы, написанные в книгах, могут игнорировать методы ООП. Вместо того, чтобы обращаться к власти (одна книга), я обращусь к вашему чувству разума: потому что статические фабрики обойти явное создание объектов (конструкторов), разве они не противоречат идее объектов? Заводы равносильны использованию глобальных переменных и функций - как тех методов, которые ООП пытается предотвратить. –  Scott Biggs 08.08.2015 в 19:52
  • @ScottBiggs центральной точкой OO является использование объектов для инкапсуляции данных и поведения. Я не вижу, как использование статического метода вместо конструктора для создания объектов в Java идет вразрез с этой идеей, но если вы не стесняетесь добавить свой собственный ответ на этот вопрос. Что касается эффективной Java, автор, Джошуа Блох, руководил разработкой и реализацией структуры коллекций Java; Я думаю, что мы можем позволить избирателям SO решить, стоит ли его мнение (обобщенное выше). –  NamshubWriter 08.08.2015 в 20:06
7

Обычно это делается потому, что класс, фактически созданный методом create() , может отличаться от типа, на который вы вызываете метод. то есть заводской шаблон, где метод create() возвращает конкретный подкласс, который является подходящим с учетом текущего контекста. (Например, возвращая один экземпляр, когда среда потока является Windows, а другая - Linux).

    
ответ дан Kirk Woll 04.10.2010 в 02:33
источник
  • Не должно быть только для особых случаев? Поскольку HashBiMap в Guava не выполняет никакого специального кэширования, просто возвращает новый экземпляр. И вы не можете переопределить его (или TMK издеваться над ним), потому что его статический. code.google.com/p/guava-libraries/source/browse/trunk/src/com/... –  TheLQ 04.10.2010 в 02:39
  • @TheLQ, я конкретно не говорил о библиотеке. Просто в общих чертах о том, какой метод фабрики create () можно использовать для. Кроме того, что его переопределяет? Если вы предполагаете, что сложно провести тестирование этого шаблона, я полностью согласен. –  Kirk Woll 04.10.2010 в 02:43
  • @TheLQ, о, определенно не используйте это для всего. С новым оператором ничего плохого. Просто имейте это в виду как хороший инструмент для решения определенных проблем. –  Kirk Woll 04.10.2010 в 02:54
  • Нет шаблона, который должен использоваться для всего. –  matt b 04.10.2010 в 03:43
  • Вы также не можете издеваться над конструктором (другими словами, с насмешливой точки зрения, статический заводский метод не менее гибкий, чем открытый конструктор) –  NamshubWriter 04.10.2010 в 05:06
6

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

/**
 * A number range that can be min-constrained, max-constrained, 
 * both-constrained or unconstrained.
 */

public class Range {
  private final long min;
  private final long max;
  private final boolean hasMin;
  private final boolean hasMax;

  private Range(long min, long max, boolean hasMin, boolean hasMax) {
    // ... (private constructor that just assigns attributes)
  }

  // Static factory methods

  public static Range atLeast (long min) {
    return new Range(min, 0, true, false);
  }

  public static Range atMost (long max) {
    return new Range(0, max, false, true);
  }

  public static Range between (long min, long max) {
    return new Range(min, max, true, true);
  }

  public static Range unconstrained () {
    return new Range (0, 0, false, false);
  }
}

Вы не могли бы сделать это, используя только конструкторы, так как atLeast и atMost имели бы одну и ту же подпись (они оба длинные).

    
ответ дан oksayt 04.10.2010 в 05:01
источник
3

Это называется шаблоном Factory. Где завод находится внутри самого класса. Википедия описывает это довольно хорошо , но вот несколько фрагментов.

  

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

     

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

    
ответ дан Ólafur Waage 04.10.2010 в 02:32
источник
  • Заводская модель хорошо работает в таких вещах, как BorderFactory, но я не понимаю, почему вы бы делали фабричный шаблон и свой класс в одном классе. Что вы получаете, делая это? –  TheLQ 04.10.2010 в 02:34
  • См .: code.google.com/p/guava-libraries/source/browse/trunk/src/com/... Мне любопытно, зачем использовать этот шаблон, когда вы выполняете то же самое, но с большим количеством кода. –  TheLQ 04.10.2010 в 02:40
  • @TheLQ Я обновил ответ. –  Ólafur Waage 04.10.2010 в 02:52
  • Я не верю, что это правда. Метод фабрики будет методом экземпляра. Swing использует шаблон достаточно. –  Tom Hawtin - tackline 06.10.2010 в 04:43
0

Ну, возможно, SomeClass.create() вытащить экземпляр из кеша. new SomeClass() не сделает этого без каких-либо махинаций.

Также было бы возможно, что create() вернет любое количество реализаций SomeClass . В принципе, тип фабрики dealio.

    
ответ дан Tony Ennis 04.10.2010 в 02:33
источник
0

Хотя это не применимо к этому конкретному примеру кода, практика скрытия конструктора за статическим методом Singleton Pattern . Это используется, когда вы хотите убедиться, что один экземпляр класса создан и используется повсюду.

    
ответ дан Sagar V 04.10.2010 в 04:12
источник
  • Это немного от того, что я получаю, но я думаю, что для одного метода скрытия конструктора вы могли бы это сделать. –  TheLQ 04.10.2010 в 04:18
  • Даже без одноэлементного шаблона этот способ создания объекта пригодится, если вы хотите, чтобы объект был создан только определенным образом, например: хэш-карта с оптимизированным начальным размером вместо того, чтобы полагаться на пользователя API чтобы предоставить его вам. –  Sagar V 04.10.2010 в 04:20
0

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

HashBiMap<Foo, Bar> bimap = new HashBiMap<Foo, Bar>();

HashBiMap<Foo, Bar> bimap = HashBiMap.create();

Гува также хорошо использует тот факт, что фабричные методы могут иметь полезные имена, в отличие от конструкторов. Рассмотрим ImmutableList.of , ImmutableList.copyOf , Lists.newArrayListWithExpectedSize и т. Д.

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

Наконец, фабричные методы ImmutableList возвращают (непубличные) подклассы ImmutableList , такие как EmptyImmutableList , SingletonImmutableList и RegularImmutableList в зависимости от аргументов.

Ни одна из этих вещей не возможна с конструкторами.

    
ответ дан ColinD 04.10.2010 в 04:50
источник
  • Я уверен, что HashBiMap <Foo, Bar> bimap = new HashBiMap () будет работать отлично, поскольку подразумевается <Foo, Bar>. –  TheLQ 04.10.2010 в 04:52
  • Будет работать, да, но он также будет генерировать предупреждения для использования необработанного типа вместо параметризованного типа ... вы не должны этого делать. Статические методы могут вывести параметры типа и сделать то же самое без предупреждений. –  ColinD 04.10.2010 в 04:55
  • Следует также отметить, что неверно говорить, что подразумевается <Foo, Bar>. Это не. Вы просто используете тип сырого типа, пред-дженерики. В JDK7 вы сможете использовать алмазный оператор, например, эту карту <Foo, Bar> map = new HashMap <> (), которая выводит типы, похожие на методы, используемые статическими методами. –  ColinD 04.10.2010 в 05:28
0

У меня очень интересная причина, чтобы скрыть конструктор проверить его и, пожалуйста, дайте мне знать, есть ли другая альтернатива для достижения этого

enter code here

Class A
 {
   String val;
    protected A( )
     {
     }
    protected A(String val)
     {
       this.val=val;
     }
   protected void setVal( String val)
    {
       this.val=val;
    } 
   public String getVal()
    {
      return val;
    }    
}
class B extends A
  {
     B()
     {
       super();
     }     
    public val setVal(String val)
    {
      super.val=val;    
    }
} 
class C extends A
{
    C(String val)
    {
      super(val);
    }
}
    
ответ дан Aamir 12.01.2016 в 19:40
источник
  • У вас нет фабричного метода ... и, честно говоря, я не знаю, где этот код должен быть хорошим примером скрытия конструктора. Поэтому вы должны это объяснить. –  Tom 12.01.2016 в 19:50
0

Некоторые основные причины

  • В первую очередь это дает вам возможность создать экземпляр другого класса ()
  • Возможность возврата null
  • Он позволяет вам вернуть уже существующий объект
ответ дан Levit 13.12.2017 в 12:42
источник