Подтвердить что ты не робот

Статические методы factory vs Instance (нормальные) конструкторы?

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

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

  • String.FromCharacters(chars);

  • new String(chars);

4b9b3361

Ответ 1

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

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

Недостатки:

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

Ответ 2

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

Например:

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 отличным от создания бара, но он говорит мне, что мне нужно посмотреть.

Ответ 3

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

Ответ 4

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

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

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

  1. Заводские методы помогают нам писать гибкий код. В то время как конструкторы сделали его тесно связанным.
  2. Статические фабричные методы улучшают читабельность, поскольку они позволяют создавать различные экземпляры.
  3. Вы можете вернуть кешированный объект. Например, getInstance() в синглтоне.