Prototype

chrishan·2021년 5월 16일
0

Design pattern

목록 보기
4/12

Intent

Prototype은 코드를 클래스에 종속시키지 않고 기존 객체를 복사 할 수있는 생성 디자인 패턴입니다.

Problem

개체가 있고 정확한 복사본을 만들고 싶다고 가정 해 보겠습니다. 어떻게 하시겠습니까? 먼저 동일한 클래스의 새 개체를 만들어야합니다. 그런 다음 원본 개체의 모든 필드를 살펴보고 해당 값을 새 개체에 복사해야합니다.
Nice! 하지만 문제가 있습니다. 모든 개체를 이렇게 복사 할 수 있는 것은 아닙니다. 일부 개체의 필드는 private일 수 있어 외부에서는 접근이 안 될 수 있습니다.

직접적인 접근 방식에는 또 하나의 문제가 있습니다. 복제본을 만들려면 객체의 클래스를 알아야하므로 코드가 해당 클래스에 종속됩니다. 추가 의존성이 당신을 두렵게하지 않는다면 또 다른 문제가 있습니다. 예를 들어 메서드의 매개 변수가 일부 인터페이스를 따르는 모든 개체를 허용하는 경우 개체가 따르는 인터페이스 만 알지만 구체적인 클래스는 알 수 없습니다.

Solution

프로토 타입 패턴은 복제중인 실제 개체에 복제 프로세스를 위임합니다. 패턴은 복제를 지원하는 모든 개체에 대한 공통 인터페이스를 선언합니다. 이 인터페이스를 사용하면 코드를 해당 개체의 클래스에 연결하지 않고도 개체를 복제 할 수 있습니다. 일반적으로 이러한 인터페이스에는 single clone method 만 포함됩니다.

clone method의 구현은 모든 클래스에서 매우 유사합니다. 이 메서드는 현재 클래스의 개체를 만들고 이전 개체의 모든 필드 값을 새 개체로 전달합니다. 대부분의 프로그래밍 언어에서는 개체가 동일한 클래스에 속하는 다른 개체의 개인 필드에 액세스 할 수 있으므로 개인 필드를 복사 할 수도 있습니다.

복제를 지원하는 개체를 prototype 이라고합니다. 개체에 수십 개의 필드와 수백 개의 possible configurations가 있는 경우 이를 복제하는 것이 subclassing의 대안이 될 수 있습니다.

작동 방식은 다음과 같습니다. 다양한 방식으로 구성된 일련의 개체를 만듭니다. 구성한 것과 같은 오브젝트가 필요한 경우 처음부터 새 오브젝트를 구성하는 대신 프로토 타입을 복제하기만 하면 됩니다.

Real-World Analogy

실 생활에서 prototype은 제품의 양산을 시작하기 전에 다양한 테스트를 수행하는데 사용됩니다. 그러나 이 경우 prototype은 실제 제작에 참여하지 않고 대신 수동적인 역할을 합니다.

산업용 프로토 타입은 실제로 자신을 복제하지 않기 때문에 패턴에 훨씬 더 가까운 유사점은 유사분열 세포 분열 과정입니다 (생물학, 기억하십니까?). 유사분열 후 한 쌍의 동일한 세포가 형성됩니다. 원본 셀은 프로토 타입 역할을 하며 복사본을 만드는데 적극적인 역할을 합니다.

Structure

Basic implementation


1. Prototype 인터페이스는 복제 방법을 선언합니다. 대부분의 경우 단일 복제 방법입니다.

2. Concrete Prototype 클래스는 복제 방법을 구현합니다. 원본 객체의 데이터를 복제하는 것 외에도 연결된 객체 복제, 재귀 종속성 풀기 등과 관련된 복제 프로세스의 일부 극단적인 경우를 처리할 수도 있습니다.

3. Client는 프로토타입 인터페이스를 따르는 모든 객체의 복사본을 생성할 수 있습니다.


Prototype registry implementation


1. Prototype Registry는 자주 사용하는 프로토타입에 쉽게 접근할 수 있는 방법을 제공합니다. 복사할 준비가 된 미리 빌드된 개체 집합을 저장합니다. 가장 간단한 프로토타입 레지스트리는 name → prototype 해시 맵입니다. 그러나 단순한 이름보다 더 나은 검색 기준이 필요한 경우 훨씬 더 강력한 버전의 레지스트리를 구축할 수 있습니다.

Pseudocode

이 예제에서 prototype pattern을 사용하면 코드를 클래스에 연결하지 않고도 기하학적 객체의 정확한 복사본을 생성 할 수 있습니다.

모든 shape 클래스는 복제 방법을 제공하는 동일한 인터페이스를 따릅니다. Subclass는 자체 필드 값을 결과 객체에 복사하기 전에 부모의 cloning method를 호출 할 수 있습니다.

// Base prototype.
abstract class Shape is
    field X: int
    field Y: int
    field color: string

    // A regular constructor.
    constructor Shape() is
        // ...

    // The prototype constructor. A fresh object is initialized
    // with values from the existing object.
    constructor Shape(source: Shape) is
        this()
        this.X = source.X
        this.Y = source.Y
        this.color = source.color

    // The clone operation returns one of the Shape subclasses.
    abstract method clone():Shape


// Concrete prototype. The cloning method creates a new object
// and passes it to the constructor. Until the constructor is
// finished, it has a reference to a fresh clone. Therefore,
// nobody has access to a partly-built clone. This keeps the
// cloning result consistent.
class Rectangle extends Shape is
    field width: int
    field height: int

    constructor Rectangle(source: Rectangle) is
        // A parent constructor call is needed to copy private
        // fields defined in the parent class.
        super(source)
        this.width = source.width
        this.height = source.height

    method clone():Shape is
        return new Rectangle(this)


class Circle extends Shape is
    field radius: int

    constructor Circle(source: Circle) is
        super(source)
        this.radius = source.radius

    method clone():Shape is
        return new Circle(this)


// Somewhere in the client code.
class Application is
    field shapes: array of Shape

    constructor Application() is
        Circle circle = new Circle()
        circle.X = 10
        circle.Y = 10
        circle.radius = 20
        shapes.add(circle)

        Circle anotherCircle = circle.clone()
        shapes.add(anotherCircle)
        // The `anotherCircle` variable contains an exact copy
        // of the `circle` object.

        Rectangle rectangle = new Rectangle()
        rectangle.width = 10
        rectangle.height = 20
        shapes.add(rectangle)

    method businessLogic() is
        // Prototype rocks because it lets you produce a copy of
        // an object without knowing anything about its type.
        Array shapesCopy = new Array of Shapes.

        // For instance, we don't know the exact elements in the
        // shapes array. All we know is that they are all
        // shapes. But thanks to polymorphism, when we call the
        // `clone` method on a shape the program checks its real
        // class and runs the appropriate clone method defined
        // in that class. That's why we get proper clones
        // instead of a set of simple Shape objects.
        foreach (s in shapes) do
            shapesCopy.add(s.clone())

        // The `shapesCopy` array contains exact copies of the
        // `shape` array's children.

Applicability

코드가 복사해야 하는 객체의 구체적인 클래스에 의존하지 않아야 하는 경우 Prototype pattern을 사용합니다.

이는 코드가 일부 인터페이스를 통해 타사 코드에서 전달 된 객체와 함께 작동 할 때 많이 발생합니다. 이러한 개체의 구체적인 클래스는 알려지지 않았으며 원하는 경우에도 의존 할 수 없습니다.

Prototype pattern은 복제를 지원하는 모든 개체에 대한 작업을 위한 일반 인터페이스를 클라이언트 코드에 제공합니다. 이 인터페이스는 클라이언트 코드를 복제하는 개체의 구체적인 클래스와 독립적으로 만듭니다.


각 객체를 초기화 하는 방식만 다른 하위 클래스의 수를 줄이려면 패턴을 사용하십시오. 누군가 특정 구성으로 개체를 만들 수 있도록 이러한 하위 클래스를 만들 수 있습니다.

Prototype pattern을 사용하면 다양한 방식으로 구성된 사전 제작 된 객체 세트를 프로토 타입으로 사용할 수 있습니다.

일부 구성과 일치하는 하위 클래스를 인스턴스화 하는 대신 클라이언트는 적절한 prototype을 찾아 복제 할 수 있습니다.

How to Implement

  1. Prototype 인터페이스를 만들고 그 안에 clone method를 선언합니다. 또는 기존 클래스 계층 구조의 모든 클래스 (있는 경우)에 메서드를 추가하면 됩니다.

  2. Prototype 클래스는 해당 클래스의 객체를 인수로 받아들이는 대체 생성자를 정의해야합니다. 생성자는 전달 된 객체에서 새로 생성 된 인스턴스로 클래스에 정의 된 모든 필드의 값을 복사해야 합니다. 하위 클래스를 변경하는 경우 상위 생성자를 호출하여 상위 클래스가 전용 필드의 복제를 처리하도록 해야 합니다.

    프로그래밍 언어가 메서드 오버로딩을 지원하지 않는 경우 개체 데이터를 복사하기 위한 특수 메서드를 정의 할 수 있습니다. 생성자는 new 연산자를 호출 한 직후에 결과 객체를 전달하므로 이 작업을 수행하기 더 편리한 곳입니다.

  3. 복제 방법은 일반적으로 한 줄로 구성됩니다. 생성자의 prototype 버전으로 new 연산자를 실행합니다. 모든 클래스는 복제 방법을 명시적으로 재정의하고 new 연산자와 함께 자체 클래스 이름을 사용해야 합니다. 그렇지 않으면 복제 메서드가 부모 클래스의 개체를 생성 할 수 있습니다.

  4. 선택적으로 중앙 집중식 프로토 타입 레지스트리를 만들어 자주 사용되는 프로토 타입의 카탈로그를 저장합니다.

    레지스트리를 새 팩토리 클래스로 구현하거나 프로토 타입을 가져 오기위한 정적 메소드를 사용하여 기본 프로토 타입 클래스에 넣을 수 있습니다. 이 메서드는 클라이언트 코드가 메서드에 전달하는 검색 기준에 따라 프로토 타입을 검색해야합니다. 기준은 단순 문자열 태그이거나 복잡한 검색 매개 변수 세트 일 수 있습니다. 적절한 프로토 타입을 찾으면 레지스트리에서이를 복제하고 사본을 클라이언트에 반환해야합니다.

    마지막으로 하위 클래스의 생성자에 대한 직접 호출을 프로토 타입 레지스트리의 팩토리 메서드에 대한 호출로 바꿉니다.

Pros and Cons

O. 구체적인 클래스에 연결하지 않고 객체를 복제 할 수 있습니다.

O. 미리 빌드 된 프로토 타입을 복제하기 위해 반복되는 초기화 코드를 제거 할 수 있습니다.

O. 복잡한 개체를 보다 편리하게 제작할 수 있습니다.

O. 복잡한 개체에 대한 구성 사전 설정을 처리 할 때 상속 대신 사용할 수 있습니다.

X. 순환 참조가 있는 복잡한 개체를 복제하는 것은 매우 까다로울 수 있습니다.

Relations with Other Patterns

  • 많은 디자인은 Factory Method (덜 복잡하고 서브 클래스를 통해 더 커스터마이징 가능)를 사용하여 시작하고 Abstract Factory, Prototype 또는 Builder (더 유연하지만 더 복잡함)로 발전합니다.

  • Abstract Factory 클래스는 종종 Factory Method 집합을 기반으로 하지만 Prototype을 사용하여 이러한 클래스에서 메서드를 구성 할 수도 있습니다.

  • Prototype can help when you need to save copies of Commands into history.

  • CompositeDecorator를 많이 사용하는 디자인은 종종 Prototype을 사용하여 이점을 얻을 수 있습니다. 패턴을 적용하면 복잡한 구조를 처음부터 재구성하는 대신 복제 할 수 있습니다.

  • Prototype은 상속을 기반으로 하지 않으므로 단점이 없습니다. 반면에 Prototype은 복제 된 개체의 복잡한 초기화가 필요합니다. Factory Method는 상속을 기반으로 하지만 초기화 단계가 필요하지 않습니다.

  • 때때로 PrototypeMemento의 더 간단한 대안이 될 수 있습니다. 기록에 저장하려는 상태 인 객체가 매우 간단하고 외부 리소스에 대한 링크가 없거나 링크를 다시 설정하기 쉬운 경우에 작동합니다.

  • Abstract Factory, BuilderPrototype은 모두 Singleton으로 구현할 수 있습니다.


_원문: https://refactoring.guru/design-patterns/prototype

0개의 댓글