Реалистичный вариант использования статического метода factory?

Я знаком с идеей и преимуществами статического метода factory, как описано в Joshua Bloch Эффективная Java:

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

Теперь я пытаюсь объяснить статические методы factory для тех, кто изучает принципы Java и OO. Она лучше учится на конкретных сценариях вместо абстракций. Если она увидит образец на работе, решая какую-то проблему, она это получит. Но ей сложнее прочитать абстрактный список характеристик, подобных приведенному выше, чтобы понять, как применять шаблон.

Можете ли вы помочь мне придумать реалистичный пример использования статического метода factory, который делает его преимущества понятными, но который еще достаточно прост, чтобы показать кого-то во вводном классе Java?

У этого человека есть опыт программирования в PL/SQL, но он никогда не разбирался в образовании шаблонов ООП.

Ответ 1

Используйте javax.swing.BorderFactory в качестве примера всех трех точек.

Этот класс используется для создания границ для swing-объектов. Эти пограничные объекты можно легко повторно использовать, и этот метод factory позволяет это. Вот javadoc. Этот factory является отличным примером всех трех точек:

  • Существует несколько статических методов с разными именами типа createEmptyBorder() и createEtchedBorder().
  • Эти методы возвращают ранее созданные объекты, когда это возможно. Очень часто, что одна и та же граница будет использоваться во всех приложениях.
  • Border сам по себе является интерфейсом, поэтому все объекты, созданные с помощью этого factory, фактически являются классами, реализующими этот интерфейс.

Ответ 2

Пример вашего второго учебника - Integer.valueOf(int) (аналогично для Boolean, Short, Long, Byte). Для значений параметров от -128 до 127 этот метод возвращает экземпляр кэширования вместо создания нового Integer. Это делает (авто) бокс/распаковку более реалистичным для типичных значений.

Вы не можете сделать это с помощью new Integer(), так как JLS требует, чтобы new создавал новый экземпляр каждый раз, когда он вызывается.

Ответ 3

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

  • Поскольку ImmutableList не предоставляет никаких конструкторов public или protected, он может быть подклассифицирован в пакете, не позволяя пользователям подклассифицировать его (и, возможно, нарушить его гарантию неизменности).
  • Учитывая, что его методы factory могут возвращать специализированные подклассы, не подвергая их типам.
  • Метод ImmutableList.of() factory возвращает один экземпляр EmptyImmutableList. Это демонстрирует, как статический метод factory не нуждается в создании нового экземпляра, если он не имеет значения.
  • Свойство ImmutableList.of(E) возвращает экземпляр SingletonImmutableList, который оптимизирован, потому что он будет удерживать ровно ровно один элемент.
  • Большинство других factory методов возвращают RegularImmutableList.
  • Его статический factory метод copyOf(Collection) также не обязательно должен создавать новый экземпляр... если Collection он задан сам по себе ImmutableList, он может просто вернуть это!

Ответ 4

Не было бы Calendar.getInstance() хорошим примером? Он создает в зависимости от локали BuddhistCalendar, JapaneseImperialCalendar или по умолчанию GregorianCalendar.

Ответ 5

Вот один, который я должен был сделать некоторое время назад. На собеседовании меня попросили запрограммировать колоду карт, где их можно перетасовать. Действительно простая проблема. Я создал:

Card:
  suit
  rank

Deck:
  card[]

Я думаю, что отличительным фактором является то, что во всех случаях может быть только 52 карты. Поэтому я создал конструктор для Card() private и вместо этого создал статический factory valueOf (костюм, ранг). Это позволило мне кэшировать 52 карты и сделать неизменным. Он преподавал много важных базовых уроков в этих книгах.

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

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

Надеюсь, это поможет!

Ответ 6

Простой случай. Предположим, у вас есть класс, который управляет каким-то принтером, но ему все равно, является ли это epson, canon или что-то еще. Итак, вы просто создаете интерфейс Printer, создаете некоторые его реализации и создаете класс, который имеет только один метод: createPrinter.

Итак, код будет прост:

   public interface Printer {
       print();
    }

    class CanonPrinter implements Printer {
       print() {
    // ...
       }
    }


    public PrinterFactory {

    Printer createPrinter() {
   if (... ) {
      return new CanonPrinter();
   } else {
      return new EpsonPrinter();
   }
}
}

клиентский код:

Printer printer = PrinterFactory.createPrinter();
printer.print();

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