Статические фабричные методы против конструкторов экземпляров (нормальных)? - PullRequest
45 голосов
/ 11 октября 2008

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

Например, если вы создаете String из char[]:

  1. String.FromCharacters(chars);

  2. new String(chars);

Ответы [ 11 ]

53 голосов
/ 11 октября 2008

В Effective Java, 2-е издание , Джошуа Блох, безусловно, рекомендует первое. Есть несколько причин, которые я могу вспомнить, и, несомненно, некоторые, которые я не могу:

  • Вы можете дать методу осмысленное имя. Если у вас есть два способа конструирования экземпляра, каждый из которых принимает int, но имеет разные значения для этого int, использование обычного метода делает вызывающий код намного более читабельным.
  • Следствие первого - у вас могут быть разные фабричные методы с одним и тем же списком параметров
  • Вы можете вернуть ноль для случаев "потенциально ожидаемого сбоя", тогда как конструктор будет всегда либо возвращать значение, либо выдавать исключение
  • Вы можете вернуть тип, отличный от объявленного (например, вернуть производный класс)
  • Вы можете использовать его как фабрику, чтобы потенциально возвращать ссылку на один и тот же объект несколько раз

Недостатки:

  • В настоящее время это не так идиоматично - разработчики более привыкли видеть "новое"
  • Если вы видите «новое», вы знаете вы получаете новый экземпляр (по модулю странность, о которой я недавно упоминал )
  • Вам нужно сделать соответствующие конструкторы доступными для подклассов
  • В C # 3 вызовы конструктора могут компактно устанавливать поля / свойства с помощью выражений инициализатора объекта; функция не применяется к статическим вызовам методов
21 голосов
/ 12 октября 2008

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

Например:

public class Foo
{
   private Foo() { }

   private static List<Foo> FooList = new List<Foo>();
   public static Foo CreateFoo()
   {
      Foo f = new Foo();
      FooList.Add(f);
      return f;
   }
}

Потому что я придерживаюсь этого соглашения, если я вижу

Foo f = Foo.CreateFoo();
Bar b = new Bar();

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

10 голосов
/ 15 июня 2011

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

В любом случае, я наткнулся на цитату из интервью Билла-Веннерса с Джошем Блохом , которая мне показалась полезной:

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

Некоторые люди были разочарованы тем, что нашли этот совет в моей книге. Они читают это и сказал: «Вы так сильно спорили для общественных статических заводов, которые мы следует просто использовать их по умолчанию. "Я думаю, что единственный реальный недостаток в делает так, что это немного сбивает с толку людей, которые используются чтобы использовать конструкторы для создания их объекты. И я предполагаю, что это обеспечивает немного меньше визуального сигнала в программа. (Вы не видите новый Ключевое слово.) И еще немного трудно найти статические заводы в документация, потому что Javadoc группирует все конструкторы вместе. Но я бы сказал, что каждый должен рассмотреть статические фабрики все время, и использовать их, когда они необходимо.

Прочитав эту цитату и исследование, которое Ури упомянул *, я склонен ошибаться в пользу конструкторов, если нет веских причин поступить иначе. Я думаю, что статический фабричный метод без уважительной причины, вероятно, просто ненужная сложность и чрезмерная инженерия. Хотя я вполне могу изменить свое мнение к завтрашнему дню ...

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

9 голосов
/ 12 октября 2008

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

7 голосов
/ 11 октября 2008

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

http://www.cs.cmu.edu/~NatProg/papers/Ellis2007FactoryUsability.pdf

2 голосов
/ 12 октября 2008

Это зависит. Для языков, в которых использование конструктора экземпляра является «нормальным», я обычно использовал бы его, если у меня не было веской причины не делать этого. Это следует принципу наименьшего удивления.

Кстати, вы забыли еще один распространенный случай: конструктор null / default в сочетании с методом инициализации.

2 голосов
/ 11 октября 2008

Я лично предпочитаю видеть нормальный конструктор, так как для конструирования должны использоваться конструкторы. Однако, если есть веская причина для , а не , используйте ее, т. Е. Если FromCharacters явно заявил, что не выделяет новую память, это будет целесообразно. «Новое» в вызове имеет значение.

2 голосов
/ 11 октября 2008

Я предпочитаю конструктор экземпляров, просто потому, что это имеет для меня больше смысла, и есть меньше потенциальной двусмысленности с тем, что вы пытаетесь выразить (т.е. что, если FromCharacters - это метод, который принимает один символ). Конечно, субъективно.

2 голосов
/ 11 октября 2008

Статический метод. Тогда вы можете вернуть ноль, а не выдавать исключение (если не ссылочный тип)

1 голос
/ 12 октября 2008

Поскольку Джон Скит перефразировал Джоша Блоха, существует ряд причин, по которым метод статической фабрики предпочтительнее конструктора во многих случаях. Я бы сказал, что если класс простой, без дорогой установки или сложного использования, оставайтесь с идиоматическим конструктором. Современные JVM делают создание объектов чрезвычайно быстрым и дешевым. Если класс может быть разделен на подклассы или вы можете сделать его неизменным (большое преимущество для параллельного программирования, которое только станет более важным), то используйте метод фабрики.

Еще один совет. Не называйте фабричный метод Foo.new* или Foo.create*. Метод с этими именами всегда должен возвращать новый экземпляр, и при этом упускается одно из больших преимуществ фабричного метода. Лучшее соглашение об именах - Foo.of* или Foo.for*. Библиотека Google Guava (ранее Библиотека коллекций Google ) отлично справляется с этим, imho.

...