Шаблоны творческого проектирования на Java

Обзор Это первая статья из короткой серии, посвященной шаблонам проектирования в Java [/ design-patterns-in-java /]. Шаблоны создания В этой статье рассматриваются следующие шаблоны создания в Java: * Фабричный метод / шаблон * Абстрактная фабрика * Строитель * Прототип * Фабричный метод синглтона Фабричный метод, также часто называемый Фабричным шаблоном, является широко используемым шаблоном проектирования, который управляет объектом. создание. В этом шаблоне класс Factory создается как родительский класс для

Обзор

Это первая статья из короткой серии, посвященной шаблонам проектирования в Java .

Творческие шаблоны

В этой статье рассматриваются следующие шаблоны создания в Java:

Заводской метод

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

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

Точно так же, как SessionFactory используется для создания, обновления, удаления и управления всеми Session , так же и любая другая фабрика, отвечающая за свой набор дочерних классов.

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

Выполнение:

Давайте создадим небольшой простой проект, чтобы продемонстрировать это.

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

 public interface Animal { 
 void eat(); 
 } 

В интерфейсе есть только один метод для удобства представления точки.

Теперь давайте определим несколько классов, реализующих этот интерфейс, каждый по-своему:

 public class Dog implements Animal { 
 @Override 
 public void eat() { 
 System.out.println("Dog is eating, woof!"); 
 } 
 } 
 
 public class Cat implements Animal { 
 @Override 
 public void eat() { 
 System.out.println("Cat is eating, meow!"); 
 } 
 } 
 
 public class Rabbit implements Animal { 
 @Override 
 public void eat() { 
 System.out.println("Rabbit is eating, squeak!"); 
 } 
 } 

Примечание . Эти классы представляют собой отдельные файлы .java , они сгруппированы вместе для удобства чтения.

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

 public class AnimalFactory { 
 
 public Animal getAnimal(String animal) { 
 if(animal.equals(null)) return null; 
 
 if(animal.equalsIgnoreCase("Dog")) { 
 return new Dog(); 
 } else if(animal.equalsIgnoreCase("Cat")) { 
 return new Cat(); 
 } else if(animal.equalsIgnoreCase("Rabbit")) { 
 return new Rabbit(); 
 } 
 return null; 
 } 
 } 

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

Теперь посмотрим на результат.

 public class Main { 
 public static void main(String[] args) { 
 AnimalFactory animalFactory = new AnimalFactory(); 
 
 Animal animal = animalFactory.getAnimal("dOg"); 
 animal.eat(); 
 
 Animal animal2 = animalFactory.getAnimal("CAT"); 
 animal2.eat(); 
 
 Animal animal3 = animalFactory.getAnimal("raBbIt"); 
 animal3.eat(); 
 } 
 } 

Выполнение этого фрагмента кода даст:

 Dog is eating, woof! 
 Cat is eating, meow! 
 Rabbit is eating, squeak! 

Если вы хотите прочитать отдельную подробную статью о шаблоне проектирования фабричного метода , мы вам поможем!

Абстрактная фабрика

Шаблон проектирования « Абстрактная фабрика» основан на шаблоне « Фабрика» и действует как самая высокая фабрика в иерархии. Он представляет собой практику создания фабрики фабрик .

Этот шаблон отвечает за создание всех других фабрик в качестве своих подклассов, точно так же, как фабрики несут ответственность за создание всех своих собственных подклассов.

Выполнение:

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

Интерфейс Animal переименован в Pet и каждая реализация будет изменена:

 public class Dog implements Pet { 
 @Override 
 public void eat() { 
 System.out.println("Dog is eating, woof!"); 
 } 
 } 
 
 public class Cat implements Pet { 
 @Override 
 public void eat() { 
 System.out.println("Cat is eating, meow!"); 
 } 
 } 
 
 public class Rabbit implements Pet { 
 @Override 
 public void eat() { 
 System.out.println("Rabbit is eating, squeak!"); 
 } 
 } 

Определен новый интерфейс:

 public interface Human { 
 public void feedPet(); 
 } 

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

 public class Child implements Human { 
 @Override 
 public void feedPet() { 
 System.out.println("Child is feeding pet irresponsibly."); 
 } 
 } 
 
 public class Adult implements Human { 
 @Override 
 public void feedPet() { 
 System.out.println("Adult is feeding pet responsibly."); 
 } 
 } 
 
 public class Elder implements Human { 
 @Override 
 public void feedPet() { 
 System.out.println("Elder is overfeeding the pet."); 
 } 
 } 

На данный момент у нас есть соответствующие классы для создания AbstractFactory а также соответствующие классы Factory для этих двух групп: PetFactory и HumanFactory .

Задача AbstractFactory - это возможность предоставить эти объекты FactoryProducer , а не создавать их экземпляры:

 public abstract class AbstractFactory { 
 public abstract Pet getPet(String pet); 
 public abstract Human getHuman(String human); 
 } 

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

 public class HumanFactory extends AbstractFactory { 
 
 @Override 
 Human getHuman(String human) { 
 if(human.equals(null)) return null; 
 
 if(human.equalsIgnoreCase("chILd")) { 
 return new Child(); 
 } else if(human.equalsIgnoreCase("adult")) { 
 return new Adult(); 
 } else if(human.equalsIgnoreCase("elDeR")) { 
 return new Elder(); 
 } 
 return null; 
 } 
 
 @Override 
 Pet getPet(String pet) { 
 // don't implement 
 return null; 
 } 

 public class PetFactory extends AbstractFactory { 
 
 @Override 
 public Pet getPet(String pet) { 
 if(pet.equals(null)) return null; 
 
 if(pet.equalsIgnoreCase("Dog")) { 
 return new Dog(); 
 } else if(pet.equalsIgnoreCase("Cat")) { 
 return new Cat(); 
 } else if(pet.equalsIgnoreCase("Rabbit")) { 
 return new Rabbit(); 
 } 
 return null; 
 } 
 
 @Override 
 Human getHuman(String human) { 
 //don't implement 
 return null; 
 } 
 } 

И теперь с их помощью мы можем создать FactoryProducer который возложена ответственность за создание экземпляров соответствующих фабрик с помощью AbstractFactory :

 public class FactoryProducer { 
 public static AbstractFactory getFactory(String factory) { 
 if(factory.equalsIgnoreCase("Human")) { 
 return new HumanFactory(); 
 } else if(factory.equalsIgnoreCase("Pet")) { 
 return new PetFactory(); 
 } 
 return null; 
 } 
 } 

FactoryProducer String , FactoryProducer возвращает AbstractFactory с запрошенной дочерней фабрикой.

Теперь посмотрим на результат:

 public class Main { 
 public static void main(String[] args) { 
 
 AbstractFactory humanFactory = FactoryProducer.getFactory("Human"); 
 AbstractFactory petFactory = FactoryProducer.getFactory("Pet"); 
 
 Human human = humanFactory.getHuman("Child"); 
 human.feedPet(); 
 
 Pet pet = petFactory.getPet("Dog"); 
 pet.eat(); 
 
 Human human2 = humanFactory.getHuman("Elder"); 
 human2.feedPet(); 
 
 Pet pet2 = petFactory.getPet("Rabbit"); 
 pet2.eat(); 
 } 
 } 

Запустив этот фрагмент кода, нас встречают:

 Child is feeding pet irresponsibly. 
 Dog is eating, woof! 
 Elder is overfeeding the pet. 
 Rabbit is eating, squeak! 

Строитель

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

Инициализация объекта с более чем несколькими полями с использованием конструктора беспорядочная и подвержена человеческой ошибке.

Выполнение:

Определим класс с несколькими полями:

 public class Computer { 
 private String computerCase; 
 private String CPU; 
 private String motherboard; 
 private String GPU; 
 private String HDD; 
 private String operatingSystem; 
 private int powerSupply; 
 private int amountOfRAM; 
 
 public Computer(String computerCase, String CPU, String motherboard, String GPU, 
 String HDD, String operatingSystem, int powerSupply, int amountOfRAM) { 
 this.computerCase = computerCase; 
 this.CPU = CPU; 
 this.motherboard = motherboard; 
 this.GPU = GPU; 
 this.HDD = HDD; 
 this.operatingSystem = operatingSystem; 
 this.powerSupply = powerSupply; 
 this.amountOfRAM = amountOfRAM; 
 } 
 
 //getters and setters 
 } 

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

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

Чтобы применить его, мы вложим статический класс static Builder Computer .

Этот конструктор будет использоваться для создания наших объектов в чистом и удобочитаемом виде, в отличие от приведенного выше примера:

 public class Computer { 
 
 public static class Builder { 
 private String computerCase; 
 private String CPU; 
 private String motherboard; 
 private String GPU; 
 private String HDD; 
 private String operatingSystem; 
 private int powerSupply; 
 private int amountOfRAM; 
 
 public Builder withCase(String computerCase) { 
 this.computerCase = computerCase; 
 return this; 
 } 
 
 public Builder withCPU(String CPU) { 
 this.CPU = CPU; 
 return this; 
 } 
 
 public Builder withMotherboard(String motherboard) { 
 this.motherboard = motherboard; 
 return this; 
 } 
 
 public Builder withGPU(String GPU) { 
 this.GPU = GPU; 
 return this; 
 } 
 
 public Builder withHDD(String HDD) { 
 this.HDD = HDD; 
 return this; 
 } 
 
 public Builder withOperatingSystem(String operatingSystem) { 
 this.operatingSystem = operatingSystem; 
 return this; 
 } 
 
 public Builder withPowerSupply(int powerSupply) { 
 this.powerSupply = powerSupply; 
 return this; 
 } 
 
 public Builder withAmountOfRam(int amountOfRAM) { 
 this.amountOfRAM = amountOfRAM; 
 return this; 
 } 
 
 public Computer build() { 
 Computer computer = new Computer(); 
 computer.computerCase = this.computerCase; 
 computer.CPU = this.CPU; 
 computer.motherboard = this.motherboard; 
 computer.GPU = this.GPU; 
 computer.HDD = this.HDD; 
 computer.operatingSystem = this.operatingSystem; 
 computer.powerSupply = this.powerSupply; 
 computer.amountOfRAM = this.amountOfRAM; 
 
 return computer; 
 } 
 } 
 
 private Computer() { 
 //nothing here 
 } 
 
 //fields 
 //getters and setters 
 } 

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

Конструктор Computer сделан закрытым, поэтому единственный способ его инициализировать - через класс Builder

После Builder мы можем инициализировать объекты Computer

 public class Main { 
 public static void main(String[] args) { 
 Computer computer = new Computer.Builder() 
 .withCase("Tower") 
 .withCPU("Intel i5") 
 .withMotherboard("MSI B360M-MORTAR") 
 .withGPU("nVidia Geforce GTX 750ti") 
 .withHDD("Toshiba 1TB") 
 .withOperatingSystem("Windows 10") 
 .withPowerSupply(500) 
 .withAmountOfRam(8) 
 .build(); 
 } 
 } 

Это гораздо более понятный и подробный способ, чем писать:

 public class Main { 
 public static void main(String[] args) { 
 Computer computer = new Computer("Tower", "Intel i5", "MSI B360M-MORTAR", 
 "nVidia GeForce GTX 750ti, "Toshiba 1TB", "Windows 10", 500, 8); 
 } 
 } 

Если вы хотите прочитать отдельную подробную статью о шаблоне проектирования Builder , мы вам поможем!

Опытный образец

Шаблон «Прототип» используется в основном для минимизации затрат на создание объекта, обычно, когда крупномасштабные приложения создают, обновляют или извлекают объекты, которые требуют больших ресурсов.

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

Выполнение:

Поскольку этот шаблон клонирует объекты, было бы уместно определить для них класс:

 // to clone the object, the class needs to implement Cloneable 
 public abstract class Employee implements Cloneable { 
 
 private String id; 
 protected String position; 
 private String name; 
 private String address; 
 private double wage; 
 
 abstract void work(); 
 
 public Object clone() { 
 Object clone = null; 
 try { 
 clone = super.clone(); 
 } catch(CloneNotSupportedException ex) { 
 ex.printStackTrace(); 
 } 
 return clone; 
 } 
 //getters and setters 
 } 

Теперь, как обычно, давайте определим несколько классов, расширяющих Employee :

 public class Programmer extends Employee { 
 public Programmer() { 
 position = "Senior"; 
 } 
 @Override 
 void work() { 
 System.out.println("Writing code!"); 
 } 
 } 
 
 public class Janitor extends Employee { 
 public Janitor() { 
 position = "Part-time"; 
 } 
 @Override 
 void work() { 
 System.out.println("Cleaning the hallway!"); 
 } 
 } 
 
 public class Manager extends Employee { 
 public Manager() { 
 position = "Intern"; 
 } 
 @Override 
 void work() { 
 System.out.println("Writing a schedule for the project!"); 
 } 
 } 

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

Hashtable будет использоваться для моделирования базы данных, а предопределенные объекты будут моделировать объекты, полученные с помощью запросов:

 public class EmployeesHashtable { 
 
 private static Hashtable<String, Employee> employeeMap = new Hashtable<String, Employee>(); 
 
 public static Employee getEmployee(String id) { 
 Employee cacheEmployee = employeeMap.get(id); 
 // a cast is needed because the clone() method returns an Object 
 return (Employee) cacheEmployee.clone(); 
 } 
 
 public static void loadCache() { 
 // predefined objects to simulate retrieved objects from the database 
 Programmer programmer = new Programmer(); 
 programmer.setId("ETPN1"); 
 employeeMap.put(programmer.getId(), programmer); 
 
 Janitor janitor = new Janitor(); 
 janitor.setId("ETJN1"); 
 employeeMap.put(janitor.getId(), janitor); 
 
 Manager manager = new Manager(); 
 manager.setId("ETMN1"); 
 employeeMap.put(manager.getId(), manager); 
 } 
 } 

Чтобы увидеть результат:

 public class Main { 
 public static void main(String[] args) { 
 EmployeesHashtable.loadCache(); 
 
 Employee cloned1 = (Employee) EmployeesHashtable.getEmployee("ETPN1"); 
 Employee cloned2 = (Employee) EmployeesHashtable.getEmployee("ETJN1"); 
 Employee cloned3 = (Employee) EmployeesHashtable.getEmployee("ETMN1"); 
 
 System.out.println("Employee: " + cloned1.getPosition() + " ID:" 
 + cloned1.getId()); 
 System.out.println("Employee: " + cloned2.getPosition() + " ID:" 
 + cloned2.getId()); 
 System.out.println("Employee: " + cloned3.getPosition() + " ID:" 
 + cloned3.getId()); 
 } 
 } 

Выполнение этого фрагмента кода даст:

 Employee: Senior ID:ETPN1 
 Employee: Part-time ID:ETJN1 
 Employee: Intern ID:ETMN1 

Синглтон

Шаблон Singleton обеспечивает существование только одного экземпляра объекта во всей JVM.

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

Выполнение:

Это довольно простая реализация класса Singleton:

 public class SingletonClass { 
 
 private static SingletonClass instance = new SingletonClass(); 
 
 private SingletonClass() {} 
 
 public static SingletonClass getInstance() { 
 return instance; 
 } 
 
 public void showMessage() { 
 System.out.println("I'm a singleton object!"); 
 } 
 } 

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

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

Статический метод getInstance() используется как глобальная точка доступа для остальной части приложения.

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

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

Давайте определим код, который извлекает этот объект и запускает метод:

 public class Main { 
 public static void main(String[] args) { 
 SingletonClass singletonClass = SingletonClass.getInstance(); 
 singletonClass.showMessage(); 
 } 
 } 

Выполнение этого кода приведет к:

 I'm a singleton object! 

Заключение

При этом все шаблоны Creational Design Patterns в Java полностью покрыты с рабочими примерами.

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

comments powered by Disqus