Структурные шаблоны проектирования в Java

Обзор Это вторая статья из короткой серии, посвященной шаблонам проектирования в Java [/ design-patterns-in-java /], и является прямым продолжением предыдущей статьи - Шаблоны проектирования в Java. [https://stackabuse.com/creational-design-patterns-in-java/] Структурные шаблоны Структурные шаблоны заботятся о предоставлении решений и эффективных стандартов, касающихся составов классов и структур объектов. Кроме того, они полагаются на концепцию наследования и интерфейсов, чтобы позволить mu

Обзор

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

Структурные образцы

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

Структурные шаблоны в Java, которые рассматриваются в этой статье:

Адаптер

Шаблон адаптера, как следует из названия, адаптирует один интерфейс к другому. Он действует как мост между двумя несвязанными, а иногда даже полностью несовместимыми интерфейсами, подобно тому, как сканер действует как мост между бумагой и компьютером.

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

Выполнение

Интерфейс Builder - это наш самый общий интерфейс, и он предоставляет метод, который принимает тип здания и его местоположение:

 public interface Builder { 
 public void build(String type, String location); 
 } 

Интерфейс AdvancedBuilder предоставляет два метода: один для постройки дома, а другой для постройки небоскреба:

 public interface AdvancedBuilder { 
 public void buildHouse(String location); 
 public void buildSkyscrapper(String location); 
 } 

Эти два интерфейса не связаны. Да, они разделяют тему, но не имеют отношения к коду.

На этом этапе создается конкретный класс, реализующий интерфейс AdvancedBuilder

 public class HouseBuilder implements AdvancedBuilder { 
 @Override 
 public void buildHouse(String location) { 
 System.out.println("Building a house located in the " + location + "area!"); 
 } 
 
 @Override 
 public void buildSkyscrapper(String location) { 
 //don't implement 
 } 
 } 

И, конечно же, по той же аналогии создается еще один конкретный класс:

 public class SkyscrapperBuilder implements AdvancedBuilder { 
 @Override 
 public void buildSkyscrapper(String location) { 
 System.out.println("Building a skyscrapper in the " + location + "area!"); 
 } 
 
 @Override 
 public void buildHouse(String location) { 
 //don't implement 
 } 
 } 

А вот адаптерная часть - для соединения этих двух интерфейсов создается BuilderAdapter реализующий Builder :

 public class BuilderAdapter implements Builder { 
 AdvancedBuilder advancedBuilder; 
 
 public BuilderAdapter(String type) { 
 if(type.equalsIgnoreCase("House")) { 
 advancedBuilder = new HouseBuilder(); 
 } else if(type.equalsIgnoreCase("Skyscrapper")) { 
 advancedBuilder = new SkyscrapperBuilder(); 
 } 
 } 
 
 @Override 
 public void build(String type, String location) { 
 if(type.equalsIgnoreCase("House")) { 
 advancedBuilder.buildHouse(location); 
 } else if(type.equalsIgnoreCase("Skyscrapper")) { 
 advancedBuilder.buildSkyscrapper(location); 
 } 
 } 
 } 

Когда адаптер работает, мы наконец можем реализовать решение и использовать метод интерфейса Builder BuilderAdapter для создания поддерживаемых типов зданий.

 public class BuilderImplementation implements Builder { 
 BuilderAdapter builderAdapter; 
 
 @Override 
 public void build(String type, String location) { 
 if(type.equalsIgnoreCase("House") || type.equalsIgnoreCase("Skyscrapper")) { 
 builderAdapter = new BuilderAdapter(type); 
 builderAdapter.build(type, location); 
 } else { 
 System.out.println("Invalid building type."); 
 } 
 } 
 } 

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

 public class Main { 
 public static void main(String[] args) { 
 BuilderImplementation builderImpl = new BuilderImplementation(); 
 
 builderImpl.build("house", "Downtown"); 
 builderImpl.build("Skyscrapper", "City Center"); 
 builderImpl.build("Skyscrapper", "Outskirts"); 
 builderImpl.build("Hotel", "City Center"); 
 } 
 } 

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

 Building a house located in the Downtown area! 
 Building a skyscrapper in the City Center area! 
 Building a skyscrapper in the Outskirts area! 
 Invalid building type. 

Мост

Шаблон «Мост» используется для отделения абстрактных классов от их реализаций и служит мостом между ними. Таким образом, и абстрактный класс, и реализация могут структурно изменяться, не затрагивая другой.

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

Выполнение

Как обычно, отправной точкой является интерфейс:

 public interface FeedingAPI { 
 public void feed(int timesADay, int amount, String typeOfFood); 
 } 

После этого его реализуют два конкретных класса:

 public class BigDog implements FeedingAPI { 
 @Override 
 public void feed(int timesADay, int amount, String typeOfFood) { 
 System.out.println("Feeding a big dog, " + timesADay + " times a day with " + 
 amount + " g of " + typeOfFood); 
 } 
 } 
 
 public class SmallDog implements FeedingAPI { 
 @Override 
 public void feed(int timesADay, int amount, String typeOfFood) { 
 System.out.println("Feeding a small dog, " + timesADay + " times a day with " + 
 amount + " g of " + typeOfFood); 
 } 
 } 

С FeedingAPI интерфейса FeedingAPI создается абстрактный класс Animal

 public abstract class Animal { 
 protected FeedingAPI feedingAPI; 
 
 protected Animal(FeedingAPI feedingAPI) { 
 this.feedingAPI = feedingAPI; 
 } 
 public abstract void feed(); 
 } 

Здесь вступает в действие паттерн Bridge. Создается класс моста, который отделяет абстрактный Animal от его реализации:

 public class Dog extends Animal{ 
 private int timesADay, amount; 
 private String typeOfFood; 
 
 public Dog(int timesADay, int amount, String typeOfFood, FeedingAPI feedingAPI) { 
 super(feedingAPI); 
 this.timesADay = timesADay; 
 this.amount = amount; 
 this.typeOfFood = typeOfFood; 
 } 
 
 public void feed() { 
 feedingAPI.feed(timesADay, amount, typeOfFood); 
 } 
 } 

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

 public class Main { 
 public static void main(String[] args) { 
 Animal bigDog = new Dog(3, 500, "Meat", new BigDog()); 
 Animal smallDog = new Dog(2, 250, "Granules", new SmallDog()); 
 
 bigDog.feed(); 
 smallDog.feed(); 
 } 
 } 

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

 Feeding a big dog, 3 times a day with 500 g of Meat 
 Feeding a small dog, 2 times a day with 250 g of Granules 

Фильтр

Шаблон «Фильтр» используется, когда нам нужен способ фильтрации наборов объектов с различными настраиваемыми критериями. Мы можем объединить критерии для еще более узкого фильтра, что выполняется независимо друг от друга.

Выполнение

Начнем с Employee который мы будем фильтровать с использованием различных Criteria :

 public class Employee { 
 private String name; 
 private String gender; 
 private String position; 
 
 public Employee(String name, String gender, String position) { 
 this.name = name; 
 this.gender = gender; 
 this.position = position; 
 } 
 //getters 
 } 

Интерфейс Criteria довольно прост, и все другие конкретные критерии будут реализовывать его метод по-своему:

 public interface Criteria { 
 public List<Employee> criteria(List<Employee> employeeList); 
 } 

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

  • CriteriaMale - критерии поиска сотрудников-мужчин
  • CriteriaFemale - критерий поиска сотрудников-женщин
  • CriteriaSenior - критерий поиска старших сотрудников
  • CriteriaJunior - критерий поиска младших сотрудников
  • AndCriteria - критерий поиска сотрудников, соответствующих обоим критериям, которые мы применяем
  • OrCriteria - критерий поиска сотрудников, соответствующих любому из критериев, которые мы применяем.

Критерии: Мужской:

 public class CriteriaMale implements Criteria { 
 
 @Override 
 public List<Employee> criteria(List<Employee> employeeList) { 
 List<Employee> maleEmployees = new ArrayList<>(); 
 
 for(Employee employee : employeeList) { 
 if(employee.getGender().equalsIgnoreCase("Male")) { 
 maleEmployees.add(employee); 
 } 
 } 
 return maleEmployees; 
 } 
 } 

Простой for который добавляет всех сотрудников-мужчин в список и возвращает его.

Критерии: Женщина:

 public class CriteriaFemale implements Criteria { 
 
 @Override 
 public List<Employee> criteria(List<Employee> employeeList) { 
 List<Employee> femaleEmployees = new ArrayList<>(); 
 
 for(Employee employee : employeeList) { 
 if(employee.getGender().equalsIgnoreCase("Female")) { 
 femaleEmployees.add(employee); 
 } 
 } 
 return femaleEmployees; 
 } 
 } 

То же, что и выше, но для сотрудников-женщин.

Критерии: Старший:

 public class CriteriaSenior implements Criteria{ 
 
 @Override 
 public List<Employee> criteria(List<Employee> employeeList) { 
 List<Employee> seniorEmployees = new ArrayList<>(); 
 
 for(Employee employee : employeeList) { 
 if(employee.getPosition().equalsIgnoreCase("Senior")) { 
 seniorEmployees.add(employee); 
 } 
 } 
 return seniorEmployees; 
 } 
 } 

То же, что и выше, но проверяет должность сотрудника, а не пол.

Критерии младший:

 public class CriteriaJunior implements Criteria { 
 
 @Override 
 public List<Employee> criteria(List<Employee> employeeList) { 
 List<Employee> juniorEmployees = new ArrayList<>(); 
 
 for(Employee employee : employeeList) { 
 if(employee.getPosition().equalsIgnoreCase("Junior")) { 
 juniorEmployees.add(employee); 
 } 
 } 
 return juniorEmployees; 
 } 
 } 

То же, что и выше, но для младших сотрудников.

AndCriteria:

 public class AndCriteria implements Criteria { 
 
 private Criteria firstCriteria; 
 private Criteria secondCriteria; 
 
 public AndCriteria(Criteria firstCriteria, Criteria secondCriteria) { 
 this.firstCriteria = firstCriteria; 
 this.secondCriteria = secondCriteria; 
 } 
 
 @Override 
 public List<Employee> criteria(List<Employee> employeeList) { 
 List<Employee> firstCriteriaEmployees = firstCriteria.criteria(employeeList); 
 return secondCriteria.criteria(firstCriteriaEmployees); 
 } 
 } 

Список сотрудников фильтруется по первому критерию, а затем уже отфильтрованный список фильтруется снова по второму критерию.

OrCriteria:

 private Criteria firstCriteria; 
 private Criteria secondCriteria; 
 
 public OrCriteria(Criteria firstCriteria, Criteria secondCriteria) { 
 this.firstCriteria = firstCriteria; 
 this.secondCriteria = secondCriteria; 
 } 
 
 
 @Override 
 public List<Employee> criteria(List<Employee> employeeList) { 
 List<Employee> firstCriteriaEmployees = firstCriteria.criteria(employeeList); 
 List<Employee> secondCriteriaEmployees = secondCriteria.criteria(employeeList); 
 
 for (Employee employee : secondCriteriaEmployees) { 
 if(!firstCriteriaEmployees.contains(employee)) { 
 firstCriteriaEmployees.add(employee); 
 } 
 } 
 return firstCriteriaEmployees; 
 } 
 } 

По индивидуальным критериям составляются два списка сотрудников. Если в первом списке нет сотрудника, как во втором, этот сотрудник добавляется в список.

Таким образом, в итоге оба списка практически сливаются.

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

 public class Main { 
 public static void main(String[] args) { 
 List<Employee> employeeList = new ArrayList<>(); 
 
 //adding employees to the list 
 employeeList.add(new Employee("David", "Male", "Senior")); 
 employeeList.add(new Employee("Scott", "Male", "Senior")); 
 employeeList.add(new Employee("Rhett", "Male", "Junior")); 
 employeeList.add(new Employee("Andrew", "Male", "Junior")); 
 employeeList.add(new Employee("Susan", "Female", "Senior")); 
 employeeList.add(new Employee("Rebecca", "Female", "Junior")); 
 employeeList.add(new Employee("Mary", "Female", "Junior")); 
 employeeList.add(new Employee("Juliette", "Female", "Senior")); 
 employeeList.add(new Employee("Jessica", "Female", "Junior")); 
 employeeList.add(new Employee("Mike", "Male", "Junior")); 
 employeeList.add(new Employee("Chris", "Male", "Junior")); 
 
 //initialization of the different criteria classes 
 Criteria maleEmployees = new CriteriaMale(); 
 Criteria femaleEmployees = new CriteriaFemale(); 
 Criteria seniorEmployees = new CriteriaSenior(); 
 Criteria juniorEmployees = new CriteriaJunior(); 
 //AndCriteria and OrCriteria accept two Criteria as their constructor 
 arguments and return filtered lists 
 Criteria seniorFemale = new AndCriteria(seniorEmployees, femaleEmployees); 
 Criteria juniorOrMale = new OrCriteria(juniorEmployees, maleEmployees); 
 
 System.out.println("Male employees: "); 
 printEmployeeInfo(maleEmployees.criteria(employeeList)); 
 
 System.out.println("\nFemale employees: "); 
 printEmployeeInfo(femaleEmployees.criteria(employeeList)); 
 
 System.out.println("\nSenior female employees: "); 
 printEmployeeInfo(seniorFemale.criteria(employeeList)); 
 
 System.out.println("\nJunior or male employees: "); 
 printEmployeeInfo(juniorOrMale.criteria(employeeList)); 
 } 
 
 
 //simple method to print out employee info 
 public static void printEmployeeInfo(List<Employee> employeeList) { 
 for (Employee employee : employeeList) { 
 System.out.println("Employee info: | Name: " 
 + employee.getName() + ", Gender: " 
 + employee.getGender() + ", Position: " 
 + employee.getPosition() + " |"); 
 } 
 } 
 } 

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

 Male employees: 
 Employee info: | Name: David, Gender: Male, Position: Senior | 
 Employee info: | Name: Scott, Gender: Male, Position: Senior | 
 Employee info: | Name: Rhett, Gender: Male, Position: Junior | 
 Employee info: | Name: Andrew, Gender: Male, Position: Junior | 
 Employee info: | Name: Mike, Gender: Male, Position: Junior | 
 Employee info: | Name: Chris, Gender: Male, Position: Junior | 
 
 Female employees: 
 Employee info: | Name: Susan, Gender: Female, Position: Senior | 
 Employee info: | Name: Rebecca, Gender: Female, Position: Junior | 
 Employee info: | Name: Mary, Gender: Female, Position: Junior | 
 Employee info: | Name: Juliette, Gender: Female, Position: Senior | 
 Employee info: | Name: Jessica, Gender: Female, Position: Junior | 
 
 Senior female employees: 
 Employee info: | Name: Susan, Gender: Female, Position: Senior | 
 Employee info: | Name: Juliette, Gender: Female, Position: Senior | 
 
 Junior or male employees: 
 Employee info: | Name: Rhett, Gender: Male, Position: Junior | 
 Employee info: | Name: Andrew, Gender: Male, Position: Junior | 
 Employee info: | Name: Rebecca, Gender: Female, Position: Junior | 
 Employee info: | Name: Mary, Gender: Female, Position: Junior | 
 Employee info: | Name: Jessica, Gender: Female, Position: Junior | 
 Employee info: | Name: Mike, Gender: Male, Position: Junior | 
 Employee info: | Name: Chris, Gender: Male, Position: Junior | 
 Employee info: | Name: David, Gender: Male, Position: Senior | 
 Employee info: | Name: Scott, Gender: Male, Position: Senior | 

Композитный

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

Обычно это делается классом, который «владеет» группой объектов и предоставляет набор методов для одинакового обращения с ними, как если бы они были одним объектом.

Выполнение

Начнем с класса Employee Экземпляр этого класса будет создаваться несколько раз, чтобы сформировать группу сотрудников:

 public class Employee { 
 private String name; 
 private String position; 
 private int wage; 
 private List<Employee> coworkers; 
 
 public Employee(String name, String position, int wage) { 
 this.name = name; 
 this.position = position; 
 this.wage = wage; 
 coworkers = new ArrayList<Employee>(); 
 } 
 
 public void addCoworker(Employee employee) { 
 coworkers.add(employee); 
 } 
 
 public void removeCoworker(Employee employee) { 
 coworkers.remove(employee); 
 } 
 
 public List<Employee> getCoworkers() { 
 return coworkers; 
 } 
 
 public String toString() { 
 return "Employee : | Name: " + name + ", Position: " + position + ", Wage: " 
 + wage + " |"; 
 } 
 } 

В классе есть список Employee , это наша группа объектов, на которую мы хотим нацеливаться как на единый объект.

 public class StackAbuseJavaDesignPatterns { 
 public static void main(String[] args) { 
 Employee employee1 = new Employee("David", "Programmer", 1500); 
 Employee employee2 = new Employee("Scott", "CEO", 3000); 
 Employee employee3 = new Employee("Andrew", "Manager", 2000); 
 Employee employee4 = new Employee("Scott", "Janitor", 500); 
 Employee employee5 = new Employee("Juliette", "Marketing", 1000); 
 Employee employee6 = new Employee("Rebecca", "Sales", 2000); 
 Employee employee7 = new Employee("Chris", "Programmer", 1750); 
 Employee employee8 = new Employee("Ivan", "Programmer", 1200); 
 
 employee3.addCoworker(employee1); 
 employee3.addCoworker(employee7); 
 employee3.addCoworker(employee8); 
 
 employee1.addCoworker(employee7); 
 employee1.addCoworker(employee8); 
 
 employee2.addCoworker(employee3); 
 employee2.addCoworker(employee5); 
 employee2.addCoworker(employee6); 
 
 System.out.println(employee2); 
 for (Employee headEmployee : employee2.getCoworkers()) { 
 System.out.println(headEmployee); 
 
 for(Employee employee : headEmployee.getCoworkers()) { 
 System.out.println(employee); 
 } 
 } 
 } 
 } 

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

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

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

 Employee : | Name: Scott, Position: CEO, Wage: 3000 | 
 Employee : | Name: Andrew, Position: Manager, Wage: 2000 | 
 Employee : | Name: David, Position: Programmer, Wage: 1500 | 
 Employee : | Name: Chris, Position: Programmer, Wage: 1750 | 
 Employee : | Name: Ivan, Position: Programmer, Wage: 1200 | 
 Employee : | Name: Juliette, Position: Marketing, Wage: 1000 | 
 Employee : | Name: Rebecca, Position: Sales, Wage: 2000 | 

Декоратор

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

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

Он отличается от классического наследования тем, что выполняется во время выполнения и применяется только к отдельному экземпляру, тогда как наследование затрагивает все экземпляры и выполняется во время компиляции.

Выполнение

Следуя приведенному выше описанию, давайте определим интерфейс:

 public interface Computer { 
 void assemble(); 
 } 

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

 public class BasicComputer implements Computer { 
 @Override 
 public void assemble() { 
 System.out.print("Assembling a basic computer."); 
 } 
 } 

Теперь для класса декоратора:

 public abstract class ComputerDecorator implements Computer { 
 protected Computer computer; 
 
 public ComputerDecorator(Computer computer) { 
 this.computer = computer; 
 } 
 
 @Override 
 public void assemble() { 
 this.computer.assemble(); 
 } 
 } 

Наши конкретные классы будут расширять этот класс, наследуя его функциональность и добавляя свои собственные функции в процессе:

 public class GamingComputer extends ComputerDecorator { 
 public GamingComputer(Computer computer) { 
 super(computer); 
 } 
 
 @Override 
 public void assemble() { 
 super.assemble(); 
 System.out.print(" Adding characteristics of a gaming computer! "); 
 } 
 } 

 public class WorkComputer extends ComputerDecorator { 
 public WorkComputer(Computer computer) { 
 super(computer); 
 } 
 
 @Override 
 public void assemble() { 
 super.assemble(); 
 System.out.print(" Adding characteristics of a work computer! "); 
 } 
 } 

Когда эти конкретные классы полностью определены, мы можем наблюдать результат:

 public class Main { 
 public static void main(String[] args) { 
 Computer gamingComputer = new GamingComputer(new BasicComputer()); 
 gamingComputer.assemble(); 
 System.out.println("\n"); 
 
 Computer workComputer = new WorkComputer(new GamingComputer(new 
 BasicComputer())); 
 workComputer.assemble(); 
 } 
 } 

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

 Assembling a basic computer. Adding characteristics of a gaming computer! 
 
 Assembling a basic computer. Adding characteristics of a gaming computer! Adding characteristics of a work computer! 

Фасад

Паттерн Фасад предоставляет клиенту простой интерфейс верхнего уровня и позволяет ему получить доступ к системе, не зная никакой системной логики и внутренних механизмов.

Выполнение

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

Мы начинаем с интерфейса Animal

 public interface Animal { 
 void feed(); 
 } 

И конкретные классы, реализующие это:

 public class Lion implements Animal { 
 @Override 
 public void feed() { 
 System.out.println("The lion is being fed!"); 
 } 
 } 
 
 public class Wolf implements Animal { 
 @Override 
 public void feed() { 
 System.out.println("The wolf is being fed!"); 
 } 
 } 
 
 public class Bear implements Animal { 
 @Override 
 public void feed() { 
 System.out.println("The bear if being fed!"); 
 } 
 } 

Это реплика для класса ZooKeeper

 public class ZooKeeper { 
 private Animal lion; 
 private Animal wolf; 
 private Animal bear; 
 
 public ZooKeeper() { 
 lion = new Lion(); 
 wolf = new Wolf(); 
 bear = new Bear(); 
 } 
 
 public void feedLion() { 
 lion.feed(); 
 } 
 
 public void feedWolf() { 
 wolf.feed(); 
 } 
 
 public void feedBear() { 
 bear.feed(); 
 } 
 } 

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

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

 public class Main { 
 public static void main(String[] args) { 
 ZooKeeper zookeeper = new ZooKeeper(); 
 
 zookeeper.feedLion(); 
 zookeeper.feedWolf(); 
 zookeeper.feedBear(); 
 } 
 } 

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

 The lion is being fed! 
 The wolf is being fed! 
 The bear if being fed! 

Наилегчайший вес

Паттерн «Легковес» направлен на снижение нагрузки на JVM и ее память. Это очень важно для устройств без большого количества памяти, а также для оптимизации приложения.

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

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

Выполнение

Как обычно, начнем с интерфейса:

 public interface Attendee { 
 public void listenToConcert(); 
 } 

Конкретный класс реализует этот интерфейс:

 public class AttendeeImpl implements Attendee { 
 private String name; 
 private int age; 
 
 public AttendeeImpl(String name) { 
 this.name = name; 
 } 
 
 public void setAge(int age) { 
 this.age = age; 
 } 
 
 @Override 
 public void listenToConcert() { 
 System.out.println(name + " is listening to concert " + age + " years old!"); 
 } 
 } 

Все эти участники будут созданы AttendeeFactory и помещены в HashMap . Важно отметить, что метод создает новый AttendeeImpl если он еще не существует. С другой стороны, если он существует, метод возвращает его.

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

 public class AttendeeFactory { 
 private static final HashMap attendees = new HashMap(); 
 
 public static Attendee getAttendee(String name) { 
 AttendeeImpl attendeeImpl = (AttendeeImpl)attendees.get(name); 
 if(attendeeImpl == null) { 
 attendeeImpl = new AttendeeImpl(name); 
 attendees.put(name, attendeeImpl); 
 System.out.println("Creating a new attendee: " + name); 
 } 
 return attendeeImpl; 
 } 
 } 

И чтобы увидеть результат, мы создадим 10 участников со случайными именами из именного пула и случайным возрастом.

 public class StackAbuseJavaDesignPatterns { 
 
 private static final String[] names = {"David", "Scott", "Andrew", "Rhett"}; 
 
 public static void main(String[] args) { 
 for(int i = 0; i < 10; ++i) { 
 AttendeeImpl attendeeImpl = (AttendeeImpl) AttendeeFactory.getAttendee(getRandomName()); 
 attendeeImpl.setAge(getRandomAge()); 
 attendeeImpl.listenToConcert(); 
 } 
 } 
 
 private static String getRandomName() { 
 int randomName = new Random().nextInt(names.length); 
 return names[randomName]; 
 } 
 
 private static int getRandomAge() { 
 return (int)(Math.random()*80); 
 } 
 } 

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

 Creating a new attendee: Scott 
 Scott is listening to concert 32 years old! 
 Scott is listening to concert 1 years old! 
 Creating a new attendee: Andrew 
 Andrew is listening to concert 8 years old! 
 Creating a new attendee: Rhett 
 Rhett is listening to concert 58 years old! 
 Andrew is listening to concert 76 years old! 
 Scott is listening to concert 56 years old! 
 Rhett is listening to concert 43 years old! 
 Scott is listening to concert 51 years old! 
 Creating a new attendee: David 
 David is listening to concert 31 years old! 
 David is listening to concert 29 years old! 

Прокси

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

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

Выполнение

Определим общий интерфейс для исходного и прокси-класса:

 public interface MediaFile { 
 void printName(); 
 } 

Этот интерфейс будет реализован классом, для которого мы определим прокси-класс:

 public class MediaFileImpl implements MediaFile { 
 private String fileName; 
 
 public MediaFileImpl(String fileName){ 
 this.fileName = fileName; 
 loadFromDisk(fileName); 
 } 
 
 @Override 
 public void printName() { 
 System.out.println("Displaying " + fileName); 
 } 
 
 private void loadFromDisk(String fileName){ 
 System.out.println("Loading " + fileName); 
 } 
 } 

 public class ProxyMediaFile implements MediaFile { 
 
 private MediaFileImpl mediaFileImpl; 
 private String fileName; 
 
 public ProxyMediaFile(String fileName){ 
 this.fileName = fileName; 
 } 
 
 @Override 
 public void printName() { 
 if(mediaFileImpl == null){ 
 mediaFileImpl = new MediaFileImpl(fileName); 
 } 
 mediaFileImpl.printName(); 
 } 
 } 

Когда эти два конкретных класса закончены, давайте посмотрим на результат:

 public class Main { 
 public static void main(String[] args) { 
 MediaFile mediaFile = new ProxyMediaFile("movie.mp4"); 
 
 mediaFile.printName(); 
 mediaFile.printName(); 
 } 
 } 

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

 Loading movie.mp4 
 Displaying movie.mp4 
 Displaying movie.mp4 

Заключение

При этом все шаблоны структурного проектирования в Java полностью покрыты с рабочими примерами.

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

comments powered by Disqus

Содержание