디자인패턴 (Prototype)

백종현·2023년 4월 28일
0

Prototype 패턴

new라는 키워드를 사용해 새로운 인스턴스를 생성하는 것이 아닌, 원래 있는 인스턴스로 부터 복사해서 새로운 인스턴스를 만드는 것.

필요한 경우는 3가지가 있다.
1. 종류가 너무 많아 클래스로 정리하지 않은 경우
2. 클래스로부터 인스턴스 생성이 어려운 경우
3. Framework와 생성할 인스턴스를 분리하고 싶은 경우
: 클래스 이름을 지정해서 인스턴스를 만드는 것이 아닌, '모형' 인스턴스를 등록해놓고 그 인스턴스를 복사해서 인스턴스를 생성해서 사용.

public interface Product extends Cloneable {
    public abstract void use(String s);
    public abstract Product createCopy();
}

Cloneable을 사용하면 clone 메소드를 사용하여 자동적으로 인스턴스 복제가 가능하다.

public class Manager {
    private Map<String,Product> showcase = new HashMap<>();

    public void register(String name, Product prototype) {
        showcase.put(name, prototype);
    }

    public Product create(String prototypeName) {
        Product p = showcase.get(prototypeName);
        return p.createCopy();
    }
}

Product를 생성함으로써 구현체를 모르도록 가능하다. 즉 Product 구현체에 의존하지 않는다.

public class MessageBox implements Product {
    private char decochar;

    public MessageBox(char decochar) {
        this.decochar = decochar;
    }

    // 복사 생성자 
    public MessageBox(MessageBox prototype) {
        this.decochar = prototype.decochar;
    }

    @Override
    public void use(String s) {
        int decolen = 1 + s.length() + 1;
        for (int i = 0; i < decolen; i++) {
            System.out.print(decochar);
        }
        System.out.println();
        System.out.println(decochar + s + decochar);
        for (int i = 0; i < decolen; i++) {
            System.out.print(decochar);
        }
        System.out.println();
    }

    @Override
    public Product createCopy() {
        return new MessageBox(this);
    }
}
public class UnderlinePen implements Product {
    private char ulchar;

    public UnderlinePen(char ulchar) {
        this.ulchar = ulchar;
    }

    // 복사 생성자 
    public UnderlinePen(UnderlinePen prototype) {
        this.ulchar = prototype.ulchar;
    }

    @Override
    public void use(String s) {
        int ulen = s.length();
        System.out.println(s);
        for (int i = 0; i < ulen; i++) {
            System.out.print(ulchar);
        }
        System.out.println();
    }

    @Override
    public Product createCopy() {
        return new UnderlinePen(this);
    }
}
public class Main {
    public static void main(String[] args) {

        Manager manager = new Manager();
        UnderlinePen upen = new UnderlinePen('-');
        MessageBox mbox = new MessageBox('*');
        MessageBox sbox = new MessageBox('/');

        manager.register("strong message", upen);
        manager.register("warning box", mbox);
        manager.register("slash box", sbox);

        Product p1 = manager.create("strong message");
        p1.use("Hello, world.");

        Product p2 = manager.create("warning box");
        p2.use("Hello, world.");

        Product p3 = manager.create("slash box");
        p3.use("Hello, world.");
    }
}

Prototype 패턴의 요소

Prototype의 역할 : Product 인터페이스가 이 역할을 하며, 인스턴스를 복사하여 새로운 인스턴스를 만들기 위한 메소드를 결정
ConcretePrototype(구체적인 원형)의 역할 : 실제로 인스턴스를 복사해서 새로운 인스턴스를 만들어 내는 메소드를 구현해낸다. 예제에서는 MessageBox 클래스와 UnderlinePen 클래스이다.
Client(이용자)의 역할 : Manager 클래스가 이 역할을 하며, 실제로 copy 해낸다. 이 예제에서는 등록과 제작이 가능하도록 했다.

왜 Prototype을 사용할까?

  1. 종류가 너무 많아서 클래스로 정리하기 힘든 경우
    위의 예제에서 Product를 구현한 객체들이 수없이 많고, 인스턴스 종류가 관리하기 어려울 정도로 많은 경우 이 패턴을 사용하여 편리하게 관리가 가능할 것이다.
  2. 클래스로부터 인스턴스 생성이 어려운 경우
    마우스로 그린 도형 인스턴스가 있다고 생각해보면, 이를 새로 만드는 것보다 인스턴스를 복사해서 새로운 인스턴스를 만드는 것이 더 편할 것이다.
  3. Framework와 생성하는 인스턴스를 분리하고 싶은 경우
    Manager의 클래스에서 create라는 클래스 이름을 new로 사용하여 만드는 대신에, 문자열을 사용하여 인스턴스를 만들 수 있는 것이 가능하게 됐다. 즉 클래스 이름의 속박으로부터 Framework를 분리할 수 있다.

실전에서 사용할만한 예시

public class Employees implements Cloneable {
    private List < String > empList;

    public Employees() {
        empLisy = new ArrayList < > ();
    }

    public Employees(List < String > list) {
        this.empList = list;
    }

    public void loadData() {
        empList = empRepository.findAll();
        예시과 같이 디비에서 가져오기
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        List < String > temp = new ArrayList < > ();
        for (String str: this.empList) {
            temp.add(str);
        }
        return new Employees(temp);
    }
}
public class PrototypePattern {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employees emps = new Employees();
        emps.loadData(); // Ann, John, Methew...

        Employees emps1 = (Employees) emps.clone();
        Employees emps2 = (Employees) emps.clone();

        List < String > list1 = emps1.getEmpList();
        list1.add("Peter");

        List < String > list2 = emps2.getEmpList();
        list2.remove("John");

        System.out.println("emps: " + emps.getEmpList());
        System.out.println("emps1: " + list1.getEmpList());
        System.out.println("emps2: " + list2.getEmpList());
    }
}

참조 : https://velog.io/@newtownboy/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85%ED%8C%A8%ED%84%B4Prototype-Pattern
Java 언어로 배우는 디자인 패턴 입문

profile
노력하는 사람

0개의 댓글