Рубрики
Без рубрики

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

Автор оригинала: David Landup.

Обзор

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

Структурные Закономерности

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

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

  • Адаптер
  • Мост
  • Фильтр
  • Составной
  • Оформитель
  • Фасад
  • Мушиный вес
  • Полномочие

Адаптер

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

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

Реализация

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

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

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

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

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

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

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
    }
}

Вот часть адаптера – для подключения этих двух интерфейсов используется адаптер Builder , реализующий 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);
    }
}

Используя интерфейс Feeding API , создается абстрактный Класс животных :

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

Вот тут-то и вступает в действие схема моста. Создается класс моста, который отделяет абстрактный 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

Фильтр

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

Реализация

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

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
}

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

public interface Criteria {
    public List criteria(List employeeList);
}

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

  • Критерии мужчины – Критерии для поиска сотрудников мужского пола
  • Критерии Женщины – Критерии для поиска сотрудниц женского пола
  • Критерии Старшие – Критерии для поиска старших сотрудников
  • Критерии младшего – Критерии для поиска младших сотрудников
  • И критерии – Критерии для поиска сотрудников, которые соответствуют обоим критериям, которые мы применяем
  • Или Критерии – Критерии для поиска сотрудников, которые соответствуют любому из критериев, которые мы применяем

Критерии Мужчины:

public class CriteriaMale implements Criteria {

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

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

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

public class CriteriaFemale implements Criteria {

    @Override
    public List criteria(List employeeList) {
        List 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 criteria(List employeeList) {
         List 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 criteria(List employeeList) {
                 List juniorEmployees = new ArrayList<>();

        for(Employee employee : employeeList) {
            if(employee.getPosition().equalsIgnoreCase("Junior")) {
                juniorEmployees.add(employee);
            }
        }
        return juniorEmployees;
    } 
}

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

И Критерии:

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 criteria(List employeeList) {
        List firstCriteriaEmployees = firstCriteria.criteria(employeeList);
        return secondCriteria.criteria(firstCriteriaEmployees);
    }
}

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

Или Критерии:

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

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

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

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

public class Main {
    public static void main(String[] args) {
        List 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 employeeList) {
        for (Employee employee : employeeList) {
            System.out.println("Employee info: | Name: " 
                    + employee.getName() + ", Gender: " 
                    + employee.getGender() + ", Position: " 
                    + employee.getPosition() + " |");
        }
    }
}

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

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

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 |

Составной

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

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

Реализация

Давайте начнем с класса Сотрудник . Этот класс будет создан несколько раз, чтобы сформировать группу сотрудников:

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

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

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();    
}

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

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 , который будет действовать как интерфейс для пользователя, который хочет кормить животных в зоопарке.

Мы начинаем с интерфейса Животное :

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!

Мушиный вес

Шаблон Flyweight направлен на снижение нагрузки на 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!");
    }
}

Все эти участники будут созданы фабрикой участников и помещены в Хэш-карту . Важно отметить, что метод создает новый объект Attendee Impl , если его еще нет. С другой стороны, если он существует, метод возвращает его.

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

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!

Полномочие

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

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

Реализация

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

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, в следующей статье рассматриваются поведенческие шаблоны проектирования .