Шаблон проектирования фабричного метода в Java

Введение Шаблоны проектирования - это набор методологий программирования, используемых в повседневном программировании. Они представляют собой решения некоторых часто встречающихся проблем в индустрии программирования, которые имеют интуитивно понятные решения. Рано или поздно настольная программа, мобильное приложение или какой-либо другой тип программного обеспечения неизбежно станут сложными и начнут проявлять определенные виды проблем. Эти проблемы обычно связаны со сложностью нашей кодовой базы, немодульностью, невозможностью разделить ce

Вступление

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

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

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

Шаблоны творческого дизайна

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

Эти шаблоны контролируют то, как мы определяем и проектируем объекты, а также то, как мы создаем их экземпляры. Некоторые инкапсулируют логику создания вдали от пользователей и обрабатывают создание ( Factory и Abstract Factory ), некоторые сосредотачиваются на процессе построения самих объектов ( Builder ), некоторые минимизируют стоимость создания ( Prototype ), а некоторые контролируют количество экземпляров на вся JVM ( синглтон ).

В частности, при разработке программного обеспечения Java очень распространены фабричный метод и абстрактная фабрика.

Шаблон фабричного метода

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

Основная идея - определить интерфейс или абстрактный класс (фабрику) для создания объектов. Хотя вместо создания экземпляра объекта создание экземпляра предоставляется его подклассам.

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

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

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

С помощью Factory Pattern логика создания объекта скрыта от клиента. Вместо того, чтобы знать точный класс объекта и создать его экземпляр с помощью конструктора, ответственность за создание объекта перекладывается на клиента.

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

Этот подход отделяет создание объекта от реализации, что способствует слабой связи и, следовательно, упрощению обслуживания и обновлений.

Мотивация

После некоторого теоретического введения давайте посмотрим на Factory Pattern на практике.

Представьте, что мы пытаемся построить собственный космический корабль. Поскольку это упрощенный пример, мы также упростим конструкцию и скажем, что наш космический корабль состоит из корпуса, Engine и спутниковой Dish :

 public class SpaceshipHangar { 
 public Spaceship createSpaceship() { 
 Spaceship ship = new Spaceship(); 
 Engine engine = new SublightEngine(); 
 Dish dish = new RoundDish(); 
 
 ship.setEngine(engine); 
 ship.setDish(dish); 
 
 return ship; 
 } 
 } 

Примечание. SublightEngine и RoundDish являются подклассами Engine и Dish соответственно.

А теперь представьте, что вы показали свой новый космический корабль другу, и вдруг ему захотелось и собственный космический корабль. Но вместо SublightEngine они хотят поставить HyperdriveEngine , а вместо RoundDish они хотят поставить SquareDish :

 public class SpaceshipHangar { 
 public Spaceship createSpaceship() { 
 Spaceship ship = new Spaceship(); 
 Engine engine = new HyperdriveEngine(); 
 Dish dish = new SquareDish(); 
 
 ship.setEngine(engine); 
 ship.setDish(dish); 
 
 return ship; 
 } 
 } 

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

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

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

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

Выполнение

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

Вместо того, чтобы жестко кодировать создание объекта в createSpaceship() с new операторами, мы создадим Spaceship и реализуем его с помощью пары различных конкретных классов.

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

Начнем с интерфейса Spaceship

 public interface Spaceship { 
 void setEngine(Engine engine); 
 void setDish(Dish dish); 
 } 

Поскольку мы работаем с Engine и Dish , давайте быстро их определим:

 public class Engine { 
 private String model; 
 
 public Engine(String model) { 
 this.model = model; 
 } 
 
 // Getters and Setters 
 } 
 
 public class Dish { 
 private String model; 
 
 public Dish(String model) { 
 this.model = model; 
 } 
 
 // Getters and Setters 
 } 

А теперь давайте реализуем интерфейс с помощью двух конкретных реализаций, начиная с SpaceshipMk1 :

 public class SpaceshipMk1 implements Spaceship { 
 private Engine engine; 
 private Dish dish; 
 
 public SpaceshipMk1(Engine engine, Dish dish) { 
 this.engine = engine; 
 System.out.println("Powering up the Mk.1 Raptor Engine"); 
 
 this.dish = dish; 
 System.out.println("Activating the Mk.1 Satellite Dish"); 
 } 
 
 @Override 
 public void setEngine(Engine engine) { 
 this.engine = engine; 
 } 
 
 @Override 
 public void setDish(Dish dish) { 
 this.dish = dish; 
 } 
 } 

И SpaceshipMk2 :

 public class SpaceshipMk2 implements Spaceship { 
 private Engine engine; 
 private Dish dish; 
 
 public SpaceshipMk2(Engine engine, Dish dish) { 
 this.engine = engine; 
 System.out.println("Powering up the Mk.2 Raptor Engine"); 
 
 this.dish = dish; 
 System.out.println("Activating the Mk.2 Satellite Dish"); 
 } 
 
 @Override 
 public void setEngine(Engine engine) { 
 this.engine = engine; 
 } 
 
 @Override 
 public void setDish(Dish dish) { 
 this.dish = dish; 
 } 
 } 

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

 public class SpaceshipFactory { 
 public Spaceship getSpaceship(Engine engine, Dish dish) { 
 if (engine.getModel().equals("Mk.2") && dish.getModel().equals("Mk.2")) { 
 return new SpaceshipMk2(engine, dish); 
 } else if (engine.getModel().equals("Mk.1") && dish.getModel().equals("Mk.1")) { 
 return new SpaceshipMk1(engine, dish); 
 } else { 
 System.out.println("Incompatible models of engine and satellite dish."); 
 } 
 return null; 
 } 
 } 

Фабрика обычно имеет единственный метод getTypeName() с параметрами, которые вы хотите передать. Затем, используя необходимое количество if , мы проверяем, какой именно класс следует использовать для обслуживания вызова.

И с этой фабрикой, когда мы хотим создать экземпляр любого из этих двух классов космических кораблей, мы используем фабрику:

 SpaceshipFactory factory = new SpaceshipFactory(); 
 
 Engine engineMk1 = new Engine("Mk.1"); 
 Dish dishMk1 = new Dish("Mk.1"); 
 
 Engine engineMk2 = new Engine("Mk.2"); 
 Dish dishMk2 = new Dish("Mk.2"); 
 
 Spaceship spaceshipMk1 = factory.getSpaceship(engineMk1, dishMk1); 
 Spaceship spaceshipMk2 = factory.getSpaceship(engineMk2, dishMk2); 
 Spaceship spaceshipMkHybrid = factory.getSpaceship(engineMk1, dishMk2); 

Здесь вместо использования new для создания экземпляра любого космического корабля мы вызываем общий интерфейс Spaceship и используем фабрику для создания / создания экземпляров объектов. Выполнение этого кода даст:

 Powering up the Mk.1 Raptor Engine 
 Activating the Mk.1 Satellite Dish 
 Powering up the Mk.2 Raptor Engine 
 Activating the Mk.2 Satellite Dish 
 Incompatible models of engine and satellite dish. 

Примечание. В идеале у нас также должны быть фабрики по производству двигателей и тарелок, особенно если у нас есть производные типы, такие как HyperdriveEngine и SquareDish . Наличие нескольких фабрик приведет к new нескольких новых ключевых слов, что противоречит тому, что означает фабричный метод.

В чем же тогда исправление? Разве мы не сделали круговой объезд и не столкнулись с той же проблемой?

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

Плюсы и минусы

Плюсы

  • Допускает слабосвязанный код, делая изменения менее разрушительными
  • Простота модульного тестирования и имитации, поскольку код не связан

Минусы

  • Делает код менее читаемым, поскольку весь код создания объектов находится за слоем абстракции.
  • При использовании с абстрактным шаблоном фабрики (фабрика фабрик) код быстро становится громоздким, но функциональным.

Заключение

Фабричный метод и другие шаблоны проектирования - это проверенные и проверенные на практике методы. Независимо от того, используется ли он в личных проектах или в очень крупных отраслевых кодовых базах. Они предлагают умные решения некоторых типичных проблем и побуждают разработчиков и целые команды сначала заниматься архитектурным дизайном, а затем программированием. Это почти всегда приводит к более качественному коду, а не сразу к программированию.

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

comments powered by Disqus