본문으로 바로가기
728x90
반응형
728x170

안녕하세요. 오늘은 Prototype 패턴에 대해 알아볼 것인데요.

 

보통 인스턴스를 작성할 때 

new Instance();

이런식으로 new라는 Java언어의 키워드를 사용해서 클래스 이름을 지정해서 인스턴스를 생성하실 겁니다.

이 처럼 new로 인스턴스를 만들 경우에 클래스 이름도 반드시 지정해야 하지만 클래스 이름을 지정하지 않고 인스턴스를 생성 할 때도 있어요.

 

  1. 종류가 너무 많아 클래스로 정리되지 않는 경우
  2. 클래스로부터 인스턴스 생성이 어려운 경우
  3. framework와 생성할 인스턴스를 분리하고 싶은 경우

가 그 예인데요.

 

1번의 경우 취급하는 오브젝트의 종류가 너무 많아서 각각을 별도의 클래스로 만들어 다수의 소스 파일을 작성해야 하는 경우를  말하구요.

 

2번의 경우 생성하고 싶은 인스턴스가 복잡한 작업을 거쳐 만들어지기 때문에 클래스로부터 만들기가 매우 어려운 경우인데요.

예를 들어 어떤 그래픽 툴로 사용자가 마우스를 조작해 만든 도형을 나타내는 인스턴스가 있다 가정할 때 사용자의 조작으로 생성되는 인스턴스를 프로그래밍해서 만드는 것은 곤란하기 때문에 사용자의 조작으로 생성되는 인스턴스를 다시 만들고 싶을 때는 지금 만든 인스턴스를 일단 저장해두고 만들고 싶은 경우에 그것을 복사합니다.

 

3번의 경우 인스턴스를 생성할 때의 framework를 특정 클래스에 의존하지 않도록 만들고 싶은 경우를 의미하는데요.

이 경우 클래스 이름을 지정하여 인스턴스를 만드는 것이 아니라 이미 모형이 되는 인스턴스를 등록해두고, 그 등록된 인스턴스를 등록해 두고, 그 등록된 인스턴스를 복사해서 인스턴스를 생성합니다.

 

쉽게 얘기해서 인스턴스로부터 다른 인스턴스를 만드는 것은 복사기를 사용해서 서류를 복사하는 일과 비슷한데요.

원래의 서류가 어떻게 만들어졌는지 몰라도 복사기로 같은 서류를 몇 장이고 만들 수 있죠.

 

그래서 이번엔 클래스로부터 인스턴스를 생성하는 것이 아니라 인스턴스로부터 별도의 인스턴스를 만드는 Prototype 패턴에 대해 알아보겠습니다.

 

Prototype은 원형, 모범을 의미합니다. 원형이 되는 인스턴스, 모범이 되는 인스터스를 기초로 새로운 인스턴스를 만드는 것인데요.

Java 언어에서는 복제를 하는 조작을 Clone이라고 부르는데 Java언어의 clone메소드와 cloneable 인터페이스의 사용방법에 대해 알아보죠.


오늘 만들어 볼 예제 프로그램은 문자열을 테두리 선으로 감싸거나 밑줄을 표시하는 프로그램입니다.


먼저 Product 인터페이스를 작성해보죠.

package framework;

public interface Product extends Cloneable{
	public abstract void Use(String s);
	public abstract Product CreateClone();
}

Product 인터페이스는 framework 패키지에 속해 있어 인스턴스를 복제하는 일을 수행하며,  java.lang.Cloneable인터페이스를 상속하고 있고, 복제를 가능하게 합니다.  이 인터페이스를 구현하고 있는 클래스의 인스턴스는 clone메소드를 사용해서 자동적으로 복제 할 수 있고,  Use 메소드는 사용하기 위한 것인데요. 무엇을 사용하는가를 의미하는지는 하위 클래스 구현에 맡겨져 있어요.

 

그 다음 Manager 클래스를 작성해 볼 것인데요. 

package framework;
import java.util.*;

public class Manager {
	private HashMap 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();
	}
}

Product 인터페이스와 마찬가지로 Manager 클래스는 framework 패키지에 속해 있어 인스턴스를 복제하는 일을 수행합니다.

Product 인터페이스를 이용해 인스턴스의 복제를 실행하는데요.

Showcase 필드는 인스턴스의 이름과 인스턴스의 대응관계를 java.util.HashMap으로 표현하였습니다.

 

Register 메소드에서 제품의 이름과 Product 인터페이스가 주어지면 그 한 쌍을 Showcase에 등록하는데요. 여기서 인수에 전달되는 Product형의 proto는 실제 클래스는 몰라도 Product 인터페이스를 구현한 클래스, 그러니까 Use메소드나 CreateClone 메소드를 호출 할 수 있는 인스턴스라는 것을 알 수 있어요.

 

Product 인터페이나 Manager 클래스의 소스에 MessageBox 클래스나 UnderlinePen클래스의 이름이 나오지 않는 이유는 Procduct와 Manager는 그들의 클래스와는 독립적으로 수정할 수 있기 때문입니다.이렇게 되면 소스 안에 이름을 쓰면 그 클래스와 밀접한 관계가 생기게 되는데 이는 아주 중요한 포인트 인데요.Manager 클래스에서는 구체적인 개개의 클래스 이름을 쓰지 않고 단지 Product라는 인터페이스 이름만 사용 되고 있어요.이 인터페이스가 Manager 클래스와 다른 클래스의 다리 역할을 하는 셈이죠.

 

이번에는 하위 클래스들을 작성해 볼 것인데요. 그 중 하나인 MessageBox 클래스를 작성해볼게요.

package bami;
import framework.*;

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;
	}
}

MessageBox 클래스는 Product인터페이스를 Implements하고 있는 모습을 볼 수 있는데요.DecoChar 필드는 문자열을 둘러싸는 것이고, Use 메소드는 주어진 문자열을 Decochar로 둘러싸주는 메소드입니다.그래서 Decochar가 #인 경우 Bami라는 문자열이 Use 메소드에 주어졌다면

########
# Bami #
########

와 같이 표시 되는 것이죠.

 

CreateClone메소드는 자기 자신을 복제하는 메소드이고, 여기에서 호출하고 있는 clone 메소드는 Java언어 사양에 규정되어 있고, 마찬가지로 자기 자신의 복제를 생성하는 메소드입니다. 복제를 생성할 때 인스턴스를 가지고 있는 필드의 값도 그대로새로운 인스턴스에 복사 되는데요. clone 메소드로 복사가 가능한 것은 java.lang.Cloneble 인터페이스를 구현하고 있는 클래스 뿐이에요.

 

그리고 이 인터페이스가 구현되고 있지 않은 경우에는 CloneNotSupportedException 이 발생하기 때문에 try ~ catch로 잡아둘 필요도 없죠.

 

그리고 MessageBox 클래스에서는 Product 인터페이스만 구현되고 있지만  Product 인터페이스는 java.lang.Cloneable 인터페이스를 확장한 것이기 때문에 CloneNotSupportedException이 발생하는 경우는 없죠.

java.lang.Cloneable 인터페이스는 단지 표식을 붙이기 위해 사용하기 때문에 이 인터페이스가 선언하고 있는 메소드도 없습니다.

 

Java 언어의 clone 메소드는 자신의 클래스나 하위 클래스에서만 호출할 수 있기 때문에 다른 클래스의 요청으로 복제를 하는 경우엔 CreateClone과 같은 메소드를 이용해 clone을 기술할 필요가 있게 되죠.

 

그 다음 UnederlinePen 클래스를 작성해보죠.

package bami;
import framework.*;

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;
	}
}

UnderlinePen 클래스는 MessageBox 클래스와 비슷한 동작을 하지만 Ulchar 라는 필드가 밑줄로 사용됩니다.

Use 메소드는 주어진 문자열을 이중 인용부호로 묶으면서 문자열 부분에 밑줄을 그리게 됩니다. 

Ulchar가 '_' 이고, bami라는 문자열이 Use 메소드에 주어지면

"bami"
______

이런식으로 표시되죠.

 

그 다음 Main 클래스를 작성해봅시다.

package bami;
import framework.*;

public class Main {
	public static void main(String[] args) {
		// 여기에서 생성 할 준비
		Manager manager = new Manager();
		UnderlinePen ulpen = new UnderlinePen('_');
		MessageBox msgbox = new MessageBox('#');
		MessageBox smsgbox = new MessageBox('*');
		manager.Register("Strong Message", ulpen);
		manager.Register("Shap Message Box", msgbox);
		manager.Register("Second Message Box", smsgbox);
		
		// 여기에서 생성
		Product p1 = manager.Create("Strong Message");
		p1.Use("bami");
		Product p2 = manager.Create("Shap Message Box");
		p2.Use("bami");
		Product p3 = manager.Create("Second Message Box");
		p3.Use("bami");
	}
}

Main클래스 에서는 Manager의 인스턴스를 만들어줍니다. 그리고 Manager의 인스턴스에 대해서 UnderlinePen의 인스턴스와 MessageBox의 인스턴스에 이름을 붙여 등록해줍니다.

 

실행화면

 

위와 같은 형태로 파일 생성해주시면 됩니다.

 


Prototye의 역할은 인스턴스를 복사하여 새로운 인스턴스를 만들기 위한 메소드를 결정하는데 예제 프로그램에선 Product가 그 역할을 합니다.

 

그리고 ConcreatePrototype의 역할은 인스턴스를 복사해서 새로운 인스턴스를 만드는 메소드를 실제로 구현하는데요.

예제 프로그램에서는 MessgeBox 클래스와 UnderlinePen 클래스가 이 역할을 하죠.

 

Client의 역할은 인스턴스를 복사하는 메소드를 이용해서 새로운 인스턴스를 만드는데요. 예제 프로그램에서는 Manager 클래스가 이 역할을 하게 되죠.

 

참조 : Java언어로 배우는 디자인 패턴 입문

728x90
반응형
그리드형

댓글을 달아 주세요