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

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

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

Некоторые приложения загружают данные из источника данных, и это может быть что угодно из базы данных, файла и т. Д. Если вашему приложению необходимо загружать данные, то загрузка той же информации каждый раз, когда вы создаете новый объект, может занять много времени и / или памяти. интенсивный в зависимости от размера. Вот здесь и пригодится паттерн-прототип.

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

Чтобы объект можно было клонировать, он должен реализовать интерфейс Cloneable. Нам нужно переопределить метод clone, чтобы выполнить глубокую копию клонируемого объекта. Если вы не знакомы с концепцией клонирования, я предлагаю вам ознакомиться с Разницами между поверхностным и глубоким копированием.

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

public class Staff implements Cloneable {
    private List<String> staffNames;
public Staff(){
        this.staffNames = new ArrayList<>();
        loadStaffNames();
    }
//simulates a database call to get all staff names
    private void loadStaffNames(){
        this.staffNames.add("Michael Bublé");
        this.staffNames.add("Frank Sinatra");
        this.staffNames.add("Mile Davis");
        this.staffNames.add("Louis Armstrong");
        this.staffNames.add("Nat King Cole");
    }
    public List<String> getStaffNames(){
        return this.staffNames;
    }
    
    public String findStaffByName(String name){
        return this.staffNames
                .stream()
                .filter(x -> x.equalsIgnoreCase(name))
                .findFirst()
                .orElse(null);
    }
    @Override
    public Object clone() throws CloneNotSupportedException{
        Staff copiedStaff = (Staff)super.clone();
        this.staffNames.stream().forEach(
                x -> copiedStaff.staffNames.add(x));
        return copiedStaff;
    }
}

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

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

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

Затем вы можете использовать свой класс Staff, и клон будет таким.

public static void main(String[] args)  {
    try {
        Staff staff = new Staff();
        System.out.println("number of staff in: "
                    + staff.getStaffNames().size());
        Staff copiedStaff = (Staff)staff.clone();
        System.out.println("number of staff: "
                    + copiedStaff.getStaffNames().size());
        System.out.println(staff.hashCode());
            System.out.println(copiedStaff.hashCode());
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
}
//---------------output----------------
number of staff: 5
number of staff: 5
2114889273
1025799482

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

Спасибо, что дожили до конца. Удачного кодирования.

Предыдущий: Шаблон творческого дизайна: абстрактная фабрика

Далее: Шаблон поведенческого дизайна: цепочка ответственности