Статические фабричные методы, чтобы избежать дублирования объектов - PullRequest
1 голос
/ 13 сентября 2009

Я читал Джошуа Блоха " Эффективное руководство по языку программирования Java ".
Он объясняет, что статические фабричные методы могут использоваться, чтобы избежать ненужных дублирующихся объектов .
Я не совсем понял это.
Кто-нибудь может объяснить?

Ответы [ 7 ]

7 голосов
/ 13 сентября 2009

Один пример из реальной жизни:

Java поддерживает как примитивные, так и объектные типы для представления байта. Когда вы конвертируете примитив в объект, вы можете сделать что-то вроде:

Byte b = new Byte( (byte) 65);

Но это создаст новый экземпляр для каждого вызова. Вместо этого вы делаете:

Byte b = Byte.valueOf( (byte) 65);

При каждом вызове метод valueOf () возвращает один и тот же экземпляр объекта Byte, представляющего значение байта 65.

После 10000 вызовов в первом примере было бы создано 10000 объектов, тогда как во втором - только один, поскольку класс Byte имеет внутренний кэш объектов Byte, представляющих все числа от -128 до 127.

6 голосов
/ 13 сентября 2009

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

Рассмотрим вместо этого приложение, которое должно создавать множество объектов Date. Он создает так много объектов Date, что построение объектов Date отрицательно влияет на производительность. Таким образом, вместо вызова конструктора объекта Date, код подвергается рефакторингу для создания дат только посредством фабричного метода. Внутри этого фабричного метода проверяется карта, чтобы увидеть, была ли запрошенная дата уже создана. Если это так, то тот же объект возвращается с карты. В противном случае создается новый, помещается в карту и возвращается.

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

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

5 голосов
/ 13 сентября 2009

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

Вот (несколько глупый) пример использования статических фабричных методов для создания псевдо-конструкторов с хорошими именами. Рассмотрим этот класс:

class Person {

   public Person(Role role) {
      setRole(role);
   }

   ...
}

Без статических фабричных методов вы можете сделать что-то вроде этого:

Person employee = new Person(Role.EMPLOYEE);
Person manager = new Person(Role.MANAGER);

Вместо этого вы можете создать статические фабричные методы:

class Person {

   public static Person newEmployee() {
      return new Person(Role.EMPLOYEE);
   }

   public static Person newManager() {
      return new Person(Role.MANAGER);
   }

   private Person(Role role) {
      setRole(role);
   }

   ...
}

и вместо этого вы можете сделать что-то вроде этого:

Person employee = Person.newEmployee();
Person manager = Person.newManager();

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

Что касается ограничения создания объекта, рассмотрим какое-то странное ограничение, будто никогда не может быть более одного генерального директора:

class Person {

   private static Person singletonCEO = new Person(Role.CEO);

   public static Person newCEO() {
      return singletonCEO;
   }

   ...
}

и как это будет создано:

Person ceo1 = Person.newCEO();
Person ceo2 = Person.newCEO();

assertThat(ceo1, is(ceo2)); // JUnit 4.x

Надеюсь, эти примеры помогут.

2 голосов
/ 13 сентября 2009

Если я хорошо помню, он также приводит пример в книге. Рассмотрим Decimal. Ноль довольно часто используется. Поэтому, если вы вызовете статический метод фабрики Decimal.valueOf("0") (не знаю, является ли это действительным API, но это не имеет значения для примера), он вернет вам экземпляр Decimal, представляющий 0, и будет таким же экземпляром для любого вызова. Реализация будет примерно такой:

public class Decimal {
    private static Decimal zero = new Decimal(0);

    public static Decimal valueOf(String s) {
        if (s.equals("0")) {
            return zero;
        } else {
            return new Decimal(parse(s)); // or whatever
        }

    // rest of the class
}

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

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

2 голосов
/ 13 сентября 2009

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

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

  1. Создание объекта дорого - Существует многообработки, когда объект создается, поэтому создание объекта более одного раза нежелательно.(Это также относится к шаблону Singleton .)

  2. У объекта нет состояния - Если между состояниями нет разницыВ некоторых случаях нет смысла создавать новый объект каждый раз.

На странице Википедии по шаблону фабричного метода есть дополнительная информация по этой теме.


Давайте рассмотрим конкретный пример.

Класс DateFormat использует статический метод getInstance для возврата *Экземпляр 1036 *, который можно использовать для форматирования Date в заданное форматирование в зависимости от локали машины.

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

В общем случае это реализуется путем создания экземпляра, если экземпляр еще не существует, а затем сохранения ссылки наэтот экземпляр.Если экземпляр нужен снова, ссылка возвращается.(Это, как правило, так же реализуется шаблон Singleton.)

Например:

class MySingleInstanceObject {

  private MySingleInstanceObject instance;

  private MySingleInstanceObject() {
    // Initialize the object.
    // This may be expensive.
  }

  public MySingleInstanceObject getInstance() {
    if (instance == null) {
      instance = new MySingleInstanceObject();
    }

    return instance;
  }
}

(К вашему сведению, приведенный выше код является примером синглтона. Кроме того, он непотокобезопасный.)

0 голосов
/ 13 сентября 2009

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

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

пример:

public class Person
{

   private Person(string firstName, string lastName)
   {
      this.FirstName = firstName;
      this.LastName = lastName;
   }

   public string FirstName {get; private set;}
   public string LastName {get; private set;}

   private static Dictionary<string, Person> objectPool = new Dictionary<string, Person>();
   private object lockObject = new object();

   public static Person CreatePerson(string firstName, string lastName)
   {
      var result = objectPool[firstName + lastName];
      Person person = null; 
      if (result != null)
      {
         return result
      }
      lock(lockObject)
      {
          person = new Person(firstName, lastName);
          objectPool.Add(firstName + lastName, person)
      }
      return person;
   }
}
0 голосов
/ 13 сентября 2009

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

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

...