Prototype Pattern
프로토타입 패턴(prototype pattern)은 소프트웨어 디자인 패턴 용어로, 생성할 객체들의 타입이 프로토타입인 인스턴스로부터 결정되도록 하며, 인스턴스는 새 객체를 만들기 위해 자신을 복제(clone)하게 된다.
프로토타입 패턴은, 추상 팩토리 패턴과는 반대로, 클라이언트 응용 프로그램 코드 내에서 객체 창조자(creator)를 서브클래스(subclass)하는 것을 피할 수 있게 해준다.
프로토타입 패턴은 새로운 객체는 일반적인 방법(예를 들어, new를 사용해서라든지)으로 객체를 생성(create)하는 고유의 비용이 주어진 응용 프로그램 상황에 있어서 불가피하게 매우 클 때, 이 비용을 감내하지 않을 수 있게 해준다.
위키피디아 - 프로토타입 패턴
프로토타입 패턴이란?
객체를 생성하는데 시간과 자원이 많이 들고, 비슷한 객체가 이미 있는 경우에 사용되는 생성 패턴이다.
프로토타입 패턴은 Original 객체를 새로운 객체에 복사하여 우리의 필요에 따라 수정하는 메커니즘을 제공한다
복사를 위해 Java에서 제공하는 clone()을 사용
public class Employees implements Cloneable {
private List<String> emList;
public Employees() {
emList = new ArrayList<String>();
}
public Employees(List<String> list) {
this.emlist = list;
}
public void loadData() {
// read all employees from database and put into the list
emList.add("David");
emList.add("Jacob");
emList.add("Puyol");
emList.add("Messi");
}
public List<String> getEmList() {
return emList;
}
@Override
public Object clone() throws CloneNotSupportedException {
List<String> temp = new ArrayList<String>();
for(String s : this.emList) {
temp.add(s);
}
return new Employees(temp);
}
}
위 코드는 clone() 메서드를 재정의하기 위해 Cloneable 인터페이스를 구현한 것을 볼 수 있는데, 여기서 사용된 clone()은 emList에 대해 깊은 복사(deep copy)를 실시한다.
public class PrototypePatternTest {
public static void main(String[] args) throws CloneNotSupportedException {
Employees ems = new Employees();
ems.loadData();
//Use the clone method to get the Employee object
Employees emsNew = (Employees) ems.clone();
Employees emsNew1 = (Employees) ems.clone();
List<String> list = emsNew.getEmList();
list.add("Kacl");
List<String> list1 = emsNew1.getEmList();
list1.remove("David");
System.out.println("ems list: " + ems.getEmList());
System.out.println("emsNew list: " + list);
System.out.println("emsNew1 list: " + list1);
}
}
ems list: [David, Jacob, Puyol, messi]
emsNew list: [David, Jacob, Puyol, messi, Kacl]
emsNew1 list: [Jacob, Puyol, messi]
만약 Employees 클래스에서 clone()을 제공하지 않았다면, DB로부터 매번 employees 리스트를 직접 가져와야 했을테고, 그로 인해 상당히 큰 비용이 발생했을 것이다.
그렇지만 프로토타입을 사용한다면 1회의 DB접근을 통해 가져온 데이터를 복사해 사용한다면 이를 쉽게 해결하며, 네트워크 접근이나 DB접근보다 훨씬 적은 비용으로 해결한다.
오히려 이 예제가 한번에 프로토타입의 장점을 느껴지는거 같아 다른 예제를 찾아봤다.
public abstract class Car implements Cloneable {
public Frame frame;
public Wheel wheel;
public Car(Frame frame, Wheel wheel) {
this.frame = frame;
this.wheel = wheel;
}
@Override
public String toString() {
return "Car{" +
"frame= " + frame +
", wheel= " + wheel +
'}';
}
@Override
protected Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
Car 클래스는 Cloneable을 구현한 복제 가능한 추상클래스이다.
public class Avante extends Car {
public Avante(Frame frame, Wheel wheel) {
super(frame, wheel);
}
public void chaneFrame(Frame frame) {
this.frame = frame;
}
@Override
public String toString() {
return "Avante{ " +
"frame= " + frame +
", wheel= " wheel +
'}';
}
}
아반떼는 생성 시점에 프레임과 휠이라는 객체타입을 받아서 초기화 된다.
인스턴스 메서드로 changeFrame()을 갖고 있어서 원하는 프레임으로 변경할 수 있다.
public class Frame {
private String name;
private String color;
public Frame(String name, String color) {
this.name = name;
this.color = color;
}
@Override
public String toString() {
return "Frame{ " +
"name= " + name +
", color= " + color +
'}';
}
}
public class Wheel {
private String name;
private int size;
public wheel(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public String toString() {
return "Wheel{ " +
"name= " + name +
", siz= " + size +
'}';
}
}
public class Main {
public static void main(String[] args) {
Frame frame = new Frame("avante V1", "blue");
Wheel wheel = new Wheel("avante V1", 18);
Avante blueAvante = new Avante(frame, wheel);
Avante newAvante = (Avante)blueAvante.clone(); // 객체 복사
Frame newFrame = new Frame("avante V2", "white");
newAvante.changeFrame(newFrame);
System.out.println(blueAvante);
System.out.println(whiteAvante);
}
}
Avante{frame=Frame{name='avante V1',color='blue'}, wheel=Wheel{name='avante V1', size=18}}
Avante{frame=Frame{name='avante V2', color='white'}, wheel=Wheel{name='avante V1', size=18}}