1. 소개

이 예제에서는 Creational Design Patterns 중 하나인 Prototype 패턴에 대해 알아봅니다 . 먼저 이 패턴에 대해 설명하고 Java로 구현해 보겠습니다.

또한 몇 가지 장점과 단점에 대해서도 논의할 것입니다.

2. 프로토타입 패턴

프로토타입 패턴은 일반적으로 클래스(프로토타입)의 인스턴스가 있고 프로토타입을 복사하여 새 객체를 만들고 싶을 때 사용됩니다 .

이 패턴을 더 잘 이해하기 위해 비유를 사용합시다.

일부 게임에서는 배경에 나무나 건물이 필요합니다. 캐릭터가 움직일 때마다 새 나무나 건물을 만들고 화면에 렌더링할 필요가 없다는 것을 깨달을 수 있습니다.

따라서 먼저 트리의 인스턴스를 만듭니다. 그런 다음 이 인스턴스(프로토타입)에서 원하는 만큼 트리를 만들고 위치를 업데이트할 수 있습니다. 게임의 새로운 레벨을 위해 나무의 색상을 변경하도록 선택할 수도 있습니다.

프로토타입 패턴은 매우 유사합니다. 새 개체를 만드는 대신 프로토타입 인스턴스를 복제하기만 하면 됩니다.

3. UML 다이어그램

프로토타입 패턴

다이어그램에서 우리는 클라이언트가 자신을 복제하고 개체를 생성하도록 프로토타입에 지시하는 것을 볼 수 있습니다. 프로토타입 은 인터페이스이며 자신을 복제하는 방법을 선언합니다. ConcretePrototype1ConcretePrototype2는  자신을 복제하는 작업을 구현합니다.

4. 시행

Java에서 이 패턴을 구현할 수 있는 방법 중 하나는 clone() 메서드를 사용하는 것입니다. 이를 위해 Cloneable 인터페이스를 구현합니다 .

복제를 시도할 때 우리는 얕은 복사본을 만들 것인지 깊은 복사본을 만들 것인지 결정해야 합니다 . 결국 요구 사항으로 귀결됩니다.

예를 들어 클래스에 원시불변 필드 만 포함되어 있는 경우 얕은 복사본을 사용할 수 있습니다.

변경 가능한 필드에 대한 참조가 포함되어 있으면 딥 카피 로 이동해야 합니다 . 복사 생성자나 직렬화 및 역직렬화 로 그렇게 할 수 있습니다 . 

앞에서 언급한 예제를 가지고 Cloneable 인터페이스를 사용하지 않고 Prototype 패턴을 적용하는 방법을 살펴보겠습니다. 이를 위해 추상 메서드 'copy'를 사용 하여 Tree 라는 추상 클래스를 생성해 보겠습니다 .

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

이제 PlasticTreePineTree 라는 두 가지 다른 Tree 구현이 있다고 가정해 보겠습니다 .

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

따라서 여기에서 우리는 Tree를 확장 하고 copy 메서드를 구현하는 클래스가 자신의 복사본을 만들기 위한 프로토타입으로 작동할 수 있음을 봅니다.

프로토타입 패턴을 사용하면 구체적인 클래스에 의존하지 않고 객체의 복사본을 만들 수도 있습니다 . 트리 List이 있고 그 복사본을 만들고 싶다고 가정해 봅시다. 다형성으로 인해 트리 유형을 몰라도 여러 복사본을 쉽게 만들 수 있습니다.

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

나무가 프로토타입에서 복제된 것을 확인하고 두 개의 서로 다른 PlasticTree 인스턴스가 있습니다 . 클론에서 위치를 업데이트하고 다른 값을 유지했습니다.

이제 트리 List을 복제해 보겠습니다.

@Test
public void givenA_ListOfTreesWhenClonedThenCreateListOfClones() {

    // create instances of PlasticTree and PineTree

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

    // ...

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

Tree 의 구체적인 구현에 의존하지 않고 여기에서 List의 전체 복사를 수행할 수 있습니다 .

6. 장점 및 단점

이 패턴은 새 개체가 기존 개체와 약간만 다를 때 유용합니다. 경우에 따라 인스턴스는 클래스에서 몇 가지 상태 조합만 가질 수 있습니다. 따라서 새 인스턴스를 만드는 대신 미리 적절한 상태로 인스턴스를 만든 다음 원할 때마다 복제할 수 있습니다 .

때로는 상태만 다른 하위 클래스를 만날 수 있습니다. 초기 상태로 프로토타입을 만든 다음 복제하여 이러한 하위 클래스를 제거할 수 있습니다.

프로토타입 패턴은 다른 모든 디자인 패턴과 마찬가지로 적절할 때만 사용해야 합니다. 개체를 복제하기 때문에 클래스가 많으면 프로세스가 복잡해져 엉망이 될 수 있습니다. 또한 순환 참조가 있는 클래스를 복제하기가 어렵습니다.

7. 결론

이 예제에서는 Prototype 패턴의 핵심 개념을 배우고 이를 Java로 구현하는 방법을 살펴보았습니다. 또한 몇 가지 장단점에 대해서도 논의했습니다.

늘 그렇듯이 이 기사의 소스 코드는 Github에서 사용할 수 있습니다 .

Generic footer banner