CS/디자인패턴

[디자인 패턴][생성 패턴] - 프로토타입 패턴

흰무 2023. 6. 13. 14:37

1. 프로토타입 패턴

 객체를 생성하는데 비용이 많이 들고, 비슷한 객체가 이미 있을 때 사용되는 패턴
 원본 객체를 새로운 객체에 복사하여 필요에 따라 수정하는 메커니즘을 제공하는 패턴
  • 사용하는 경우
    • 종류가 너무 많아 클래스로 정리할 수 없는 경우 => 소스파일을 줄일 수 있다
    • 클래스로부터 인스턴스 생성이 어려운 경우 => 이미 한번 복잡한 과정을 거쳐 만들어진 인스턴스 활용 가능(과정 생략)
    • 프레임워크와 생성하는 인스턴스를 분리하고 싶은 경우 => 등록된 인스턴스를 복사해서 생성하기 때문에, 프레임워크와 분리

 

  • 구현 방법

 Java의 경우, Java에서 제공하는 Clone 메소드와 Cloneable 인터페이스를 사용해 구현한다.

프로토타입 패턴의 클래스 다이어그램

  • 예제

Product 인터페이스 = Prototype

Mananger 클래스 = Client

MessageBox, UnderlinePen 클래스 = ConcretePrototype1,2

 

## Product 인터페이스

public interface Product extends Cloneable {
	public abstract void use(String s);
    public abstract Product createCopy();
}
## Mananger 클래스
## Product 인터페이스를 이용해 인스턴스를 복제
## import 생략

public class Manager {
	private Map<String,Product> showcase =. ew 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();
    }
}
## MessageBox 클래스
## Product 인터페이스를 구현
## use 메소드는 주어진 문자열을 decochar로 감싼다

## createCopy 메소드는 자기 자신을 복제하는 메소드
## clone 메소드는 자신의 클래스에서만 호출할 수 있으므로
## 다른 클래스의 요청으로 복제할 경우 createCopy와 같은 별도의 메소드가 필요!

public class MessageBox implements Product {
	private char decochar;

	public MessageBox(char decochar) {
    	this.decochar = 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(dechchar);
    }
    System.out.println();
}

	@Override
    public Product createCopy() {
    	Product p = null;
        try {
        	p = (Product)clone();
        } catch (CloneNotSupportedException e) {
        	e.printStackTrace();
        }
        return p;
    }
}

## UnderlinePen 클래스
## MessageBox와 같은 동작을 하나, 주어진 문자열을 감싸는 것이 아닌, 밑줄만 생성
public class UnderlinePen implements Product {
	private char ulchar;

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

	@Override
    public Product createCopy() {
    	Product p = null;
        try {
        	p = (Product)clone();
        } catch (CloneNotSupportedException e) {
        	e.printStackTrace();
        }
        return p;
    }
}
## Main 클래스
## 위의 인터페이스와 클래스들을 활용해 인스턴스를 생성/복제

public class Main {
	public static void main(String[] args) {
    	
        // 준비
        Manager mananger = 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 = manger.create("slash box");
        p3.use("Hello, world.");
    }
}

 

위의 Main 클래스를 실행하면, p1,p2,p3 모두 인스턴스가 복제되어 생성되는 것을 확인할 수 있다.

 

  • 의문점?

왜 이렇게 소스 코드 안의 클래스 이름을 쓰지 않고 분리하기 위해 노력하는 걸까?

=> 객체지향 프로그래밍의 목표 중 하나가 "부품으로서의 재사용"이기 때문

=> 소스 코드 안에 이용할 클래스의 이름이 쓰여 있으면, 클래스와 분리해서 재사용할 수 없게 된다!!