프로토타입 패턴

기존 객체를 복제하여 새 객체를 생성하는 패턴. 

 

연습 문제

아래 코드를 프로토타입 패턴으로 리팩토링 해보세요.

public class Button {
    public String x, y, color;

    public Button(String x, String y, String color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }
}

public class Main {
    public static void main(String[] args) {
        // 원본 버튼
        Button original = new Button("10", "40", "red");
        
        // 복제하려면 필드를 하나하나 복사해야 함
        Button copy = new Button(original.x, original.y, original.color);
        
    }
}

 

요구사항

  • Prototype 인터페이스: clone() 메서드 선언
  • ConcretePrototype: clone() 구현, 복사 생성자 사용
  • PrototypeRegistry (선택): 프로토타입 객체들을 저장하고 복제본 반환

 

현재 문제점

  1. 필드 직접 복사 - 필드가 많으면 복사 코드가 길어짐
  2. private 필드 - 외부에서 접근 불가하면 복사 자체가 불가능
  3. 서브클래스 - 부모 타입으로 받으면 실제 타입을 알 수 없음

 

연습문제 정답 - 간단한 Prototype 패턴

1. Prototype 인터페이스

public interface Prototype {
    Prototype clone();
}

2. ConcretePrototype 구현

public class ConcretePrototype implements Prototype {
    public String str;

    public ConcretePrototype() {}

    // 복사 생성자 - 자기 타입을 받아야 필드 접근 가능
    public ConcretePrototype(ConcretePrototype prototype) {
        this.str = prototype.str;
    }

    @Override
    public Prototype clone() {
        return new ConcretePrototype(this);
    }
}

3. SubclassPrototype 구현

public class SubclassPrototype extends ConcretePrototype {
    public String subStr;

    public SubclassPrototype() {
        super();
    }

    public SubclassPrototype(SubclassPrototype prototype) {
        super(prototype);  // 부모 필드 복사
        this.subStr = prototype.subStr;
    }

    @Override
    public Prototype clone() {
        return new SubclassPrototype(this);
    }

    @Override
    public String toString() {
        return "SubclassPrototype{" +
                "subStr='" + subStr + '\'' +
                ", str='" + str + '\'' +
                '}';
    }
}

4. 클라이언트 코드

public class Main {
    public static void main(String[] args) {
        SubclassPrototype subclassPrototype = new SubclassPrototype();
        subclassPrototype.subStr = "sub - sub";
        subclassPrototype.str = "sub - str";
        System.out.println(subclassPrototype);

        SubclassPrototype cloned = (SubclassPrototype) subclassPrototype.clone();
        cloned.subStr = "new sub";
        cloned.str = "new str";
        System.out.println(cloned);
        System.out.println(subclassPrototype);  // 원본은 변경 안 됨
    }
}

 

연습문제 정답 - PrototypeRegistry 

자주 사용하는 프로토타입을 미리 등록해두고 복제본을 반환하는 저장소.

1. Prototype 인터페이스 (확장)

public interface Prototype {
    Prototype clone();
    String getColor();
}

2. Button 구현

public class Button implements Prototype {
    public String x, y, color;

    public Button(String x, String y, String color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    public Button(Button prototype) {
        this(prototype.x, prototype.y, prototype.color);
    }

    @Override
    public String getColor() {
        return this.color;
    }

    @Override
    public Prototype clone() {
        return new Button(this);
    }
}

3. PrototypeRegistry 구현

public class PrototypeRegistry {
    private Map<String, Prototype> items = new ConcurrentHashMap<>();

    public void addItem(String id, Prototype prototype) {
        items.put(id, prototype);
    }

    public Prototype getById(String id) {
        Prototype prototype = items.get(id);
        return prototype != null ? prototype.clone() : null;
    }

    public Prototype getByColor(String color) {
        for (Prototype p : items.values()) {
            if (p.getColor().equals(color)) {
                return p.clone();
            }
        }
        return null;
    }
}

getByColor()는 읽기 전용이고 clone()으로 새 객체를 반환하기 때문에 synchronized 없이도 스레드 세이프

4. 클라이언트 코드

public class Main {
    public static void main(String[] args) {
        PrototypeRegistry registry = new PrototypeRegistry();
        registry.addItem("redBtn", new Button("10", "40", "red"));
        registry.addItem("blueBtn", new Button("20", "50", "blue"));

        // ID로 복제
        Button btn1 = (Button) registry.getById("redBtn");

        // 색상으로 복제
        Button btn2 = (Button) registry.getByColor("red");
    }
}

참조

https://refactoring.guru/ko/design-patterns/prototype

+ Recent posts