송민준의 개발노트

Prototype 패턴 본문

JAVA/디자인패턴

Prototype 패턴

송민준 2021. 8. 29. 14:16

1. 기본 개념

프로토타입의 기본 개념은 '원형'이나 '모범'을 의미한다.

이 원형이 되는 인스턴스, 모범이 되는 인스턴스를 바탕으로 새로운 인스턴스를 만드는 패턴이다.

 

2. 왜 사용하는가?

- 종류가 너무 많아서 클래스로 정리할 수 없는 경우( 포유류에는 돼지, 소 등 많다... )

- 클래스로부터 인스턴스 생성이 어려운 경우( PPT에서 만든 도형묶음을 똑같이 만드는 행위 등..)

- 프레임워크와 생성하는 인스턴스를 분리하고 싶은 경우

 => 클래스 이름으로부터 자유롭게 구현(이름이 정의되어 있으면 그 이름으로부터 자유로울 수 없음. 즉 재사용이 힘들어져 부품으로써 재이용이라는 객체지향 목표에 반하게 됨)

 

* 여기서 말하는 재사용이라는 건 java 소스파일 없이 클래스파일(.class)만 가지고 재사용이 가능하지가 중요...!

 

3. 소스로 보는

/**
 프레임워크 역할을 하는 Product, Manager
 프로토타입을 뽑아내는 MessageBox, UnderlinePen
 
 manager에 등록하는 Product 하위 객체들은 이름으로부터 자유롭다.
*/
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");
    }
}
/**
 * 인스턴스를 복사하여 새로운 인스턴스를 만들기 위한 메소드를 결정
 * 상세한 것은 하위 클래스에서 구현
 */
public interface Product extends Cloneable {
    public abstract void use(String s);
    public abstract Product createClone();
}
/**
 * 인스턴스를 복사하는 메소드를 이용해서 새로운 인스턴스를 만듬.
 */
public class Manager {
    private HashMap<String, Product> showcase = new HashMap<>();
    public void register(String name, Product proto) {
        showcase.put(name, proto);
    }
    public Product create(String protoname) {
        Product p = (Product)showcase.get(protoname);
        return p.createClone();
    }

}
/**
 * 인스턴스를 복사해서 새로운 인스턴스를 만드는 메소드를 실제 구현 1
 */
public class MessageBox implements Product {
    private char decochar;
    public MessageBox(char decochar) {
        this.decochar = decochar;
    }
    public void use(String s) {
        int length = s.getBytes().length;
        for(int i = 0; i < length + 4; i++) {
            System.out.print(decochar);
        }
        System.out.println("");
        System.out.println(decochar + " " + s + " " + decochar);
        for(int i =0; i < length + 4; i++ ) {
            System.out.print(decochar);
        }
        System.out.println("");
    }
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}
/**
 * 인스턴스를 복사해서 새로운 인스턴스를 만드는 메소드를 실제 구현 2
 */
public class UnderlinePen implements Product {
    private char ulchar;
    public UnderlinePen(char ulchar) {
        this.ulchar = ulchar;
    }
    public void use(String s) {
        int length = s.getBytes().length;
        System.out.println("\"" + s + "\"");
        System.out.print("");
        for(int i = 0; i < length; i++) {
            System.out.print(ulchar);
        }
        System.out.println("");
    }
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

 

이런 패턴을 보면 에디터 같은거 구현할 때 좋을 것 같긴 한데 좀 고민이 필요한 부분인거 같다.

 

 

+ 추가적으로 Product는 Cloneable 인터페이스를 상속 받았는데

Cloneable은 내부를 보면 메소드가 하나도 선언되어 있지 않음. 이 인터페이스는 단지 'clone에 의해 복사할 수 있다.' 라는 표시로서 사용되고 있다. 이와 같이 표시를 하는 인터페이스를 메이커 인터페이스라고 한다. 하지만 이 인터페이스 없이 clone메소드를 사용하면 CloneNotSupportedException이 발생한다.(주석 참고)

 

또한 clone 메소드는 피상적인 복사를 실행하는데 이것은 필드의 내용을 그대로 복사한다는 것이며 이게 만약 배열과 같은 참조이면 참조만 복사될 뿐이고 요소 하나하나가 복사되는 것은 아니다. 이런 피상적인 복사로 곤란할 경우엔 clone메소드를 오버라이드 해줘야한다.

'JAVA > 디자인패턴' 카테고리의 다른 글

Abstract Factory 패턴  (0) 2021.10.31
Builder 패턴  (0) 2021.08.30