인스턴스를 생설할때 new를 사용하는 것이 아니라 이미 만들어진 인스턴스를 복사해서
새로운 인스턴스를 만들어 내는 패턴이다. 이 인스턴스 생성 방식을 사용 하는 이유는
1. 종류가 너무 많아 한개의 클래스로 할 수 없는 경우
2. 클래스로부터 인스턴스를 생성하기 어려운 경우
3. 프레임워크와 생성할 인스턴스를 분리하고 싶은 경우
정도로 나눌 수 있다.
예를 보면..
Product 인터페이스와 Manager 클래스는 framework패키지에 속해있고, 인스턴스를 복제하는 일을 한다.
Manager클래스는 createClone을 호출하지만 구체적의 어느 클래스의 인스턴스인지 까지는 알지 못 하며 단지 Product인터페이스를 구현한 클래스의 인스턴스라면 복제가 가능하다.
MessageBox클래스와 UnderlinePen클래스는 둘 다 Product 인터페이스를 구현하고 있으며, 이들의 인스턴스를 만들어 Manager클래스에 등록해두면 언제든지 복제 할 수 있다.
여기서 사용된 복제는 clone()메소드를 사용하였으며, java.lang.Cloneable인터페이스를 구현하고 있는 클래스의 인스턴스는 clone메소드를 사용해서 자동적으로 자기자신을 복제 할 수 있다.
코드를 보면
1. Product인터페이스
package framework;
public interface Product extends Cloneable {
public abstract void use(String s);
public abstract Product createClone();
}
2.Manager클래스
package framework;
import java.util.*;
public class Manager {
private Hashtable showcase = new Hashtable();
public void register(String name, Product proto) {
showcase.put(name, proto);
}
public Product create(String protoname) {
Product p = (Product)showcase.get(protoname); //Upcasting
return p.createClone();
}
}
이름과 인스턴스의 대응관계를 해쉬 테이블을 사용해 저장하며, create에서는 Product 인터페이스를 구현한 클래스의 인스턴스를 복사하여 return해주고 있다.
3.MessageBox
String을 주어진 char형으로 감싸 출력하는 클래스이다.
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;
}
}
Product 인터페이스만 구현되어 있지만, Product인터페이스는 java.lang.Cloneable인터페이스를 확장한 것이기 때문에 clone()사용이 가능하다. 또한 clone()메소드는 자신의 클래스(및 하위 클래스)에서만 호출할 수 있기 때문이 (Protected형) 다른 클래스의 요청으로 복제하는 경우에는
다른 메소드로 clone을 감싸줄 필요가 있다.
4.UnderlinePen클래스
주어진 캐릭터로 String에 밑줄을 그어 출력하는 클래스이다.
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;
}
}
Main클래스(테스트를 위함)
import framework.*;
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.");
}
}
이제 하나를 잡아서 따라가보자..
Main에서 최초로 Manager manager = new Manager();
클래스를 생성했고,
UnderlinePen upen = new UnderlinePen('~');
upen이란 이름으로 UnderlinePen클래스의 인스턴스를 생성해주고 있다.
그리고,
manager.register("strong message", upen);
Manager클래스의 register 메소드를 사용하여 해쉬 테이블에 strong message라는 이름으로
upen인스턴스를 연결시켜 주고있다.
Product p1 = manager.create("strong message");
Product형의 p1은 manager인스턴스의 create메소드를 사용하여 그 값을 받는데
create("strong message")를 살펴보면
public Product create(String protoname) {
Product p = (Product)showcase.get(protoname); //Upcasting
return p.createClone();
}
아까 register를 사용하여 해쉬테이블에 등록한 인스턴스들 중 protoname즉 strong message라는 이름을 가진 것을 꺼내어 Product p에 대입시키고 있으며(Product형으로 Upcasting)
, 이 p의 createClone()을 실행
시킨 후 그 리턴값을 리턴해주고 있다.
crateClone()을 보면
public Product createClone() {Product p = null;
try {
p = (Product)clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
(Product)clone(); 자기 자신을 복사하여 (Product형으로 Upcastiong된 UnderlinePen클래스의 인스턴스) 그 리턴값을 Prodcut형으로 DownCasting 해주고(clone의 리턴형이 object형이므로)
그 값을 리턴해준다. 즉 Product형으로 형변환된 복제된 UnderlinePen클래스의 인스턴스가 리턴되는 것이다.
그래서 Main에서 Product p1 = manager.create("strong message");
이 문장에서 p1에는 Product형으로 형변환된 복제된 UnderlinePen클래스의 인스턴스가 대입되게 되고 p1.use를 실행 하면
p1.use("Hello, world.");
Hello,world밑에 밑줄이 그어져서 출력되게 된다.