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

Шаблон прототипа в Java

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

Автор оригинала: Vivek Balasubramaniam.

1. введение

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

Мы также обсудим некоторые его преимущества и недостатки.

2. Образец прототипа

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

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

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

Итак, сначала мы создаем экземпляр дерева. Затем мы можем создать из этого экземпляра (прототипа) столько деревьев, сколько захотим, и обновить их позиции. Мы также можем изменить цвет деревьев для нового уровня в игре.

Шаблон прототипа очень похож. Вместо создания новых объектов нам просто нужно клонировать прототипический экземпляр.

3. Диаграмма UML

На диаграмме мы видим, что клиент говорит прототипу клонировать себя и создать объект. Prototype является интерфейсом и объявляет метод для клонирования самого себя. ConcretePrototype1 и ConcretePrototype2 реализуют операцию клонирования самих себя.

4. Реализация

Одним из способов реализации этого шаблона в Java является использование метода clone () . Для этого мы реализуем интерфейс Cloneable .

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

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

Если он содержит ссылки на изменяемые поля, мы должны перейти к глубокой копии . Мы могли бы сделать это с помощью |/конструкторов копирования или сериализации и десериализации .

Давайте возьмем пример, о котором мы упоминали ранее, и перейдем к тому, как применить шаблон прототипа без использования интерфейса Cloneable . Для этого давайте создадим абстрактный класс с именем Дерево с помощью абстрактного метода ‘copy’ .

public abstract class Tree {
    
    // ...
    public abstract Tree copy();
    
}

Теперь предположим, что у нас есть две разные реализации Дерева под названием Пластиковое дерево и Сосна :

public class PlasticTree extends Tree {

    // ...

    @Override
    public Tree copy() {
        PlasticTree plasticTreeClone = new PlasticTree(this.getMass(), this.getHeight());
        plasticTreeClone.setPosition(this.getPosition());
        return plasticTreeClone;
    }

}
public class PineTree extends Tree {
    // ...

    @Override
    public Tree copy() {
        PineTree pineTreeClone = new PineTree(this.getMass(), this.getHeight());
        pineTreeClone.setPosition(this.getPosition());
        return pineTreeClone;
    }
}

Итак, здесь мы видим, что классы, которые расширяют Дерево и реализуют метод copy , могут выступать в качестве прототипов для создания копии самих себя.

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

5. Тестирование

Теперь давайте проверим это:

public class TreePrototypesUnitTest {

    @Test
    public void givenAPlasticTreePrototypeWhenClonedThenCreateA_Clone() {
        // ...

        PlasticTree plasticTree = new PlasticTree(mass, height);
        plasticTree.setPosition(position);
        PlasticTree anotherPlasticTree = (PlasticTree) plasticTree.copy();
        anotherPlasticTree.setPosition(otherPosition);

        assertEquals(position, plasticTree.getPosition());
        assertEquals(otherPosition, anotherPlasticTree.getPosition());
    }
}

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

Теперь давайте клонируем список деревьев:

@Test
public void givenA_ListOfTreesWhenClonedThenCreateListOfClones() {

    // create instances of PlasticTree and PineTree

    List trees = Arrays.asList(plasticTree, pineTree);
    List treeClones = trees.stream().map(Tree::copy).collect(toList());

    // ...

    assertEquals(height, plasticTreeClone.getHeight());
    assertEquals(position, plasticTreeClone.getPosition());
}

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

6. Преимущества и недостатки

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

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

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

7. Заключение

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

Как обычно, исходный код этой статьи доступен на Github .