Prototype 패턴 개요
Prototype 패턴은 객체 생성 비용을 줄이고, 새로운 객체를 생성하기 위해 기존 객체를 복제하는 디자인 패턴입니다. Prototype 패턴은 다음과 요소를 가지고 있습니다:
- Prototype 인터페이스 또는 추상 클래스: 복제 기능을 선언하는 메서드를 정의합니다.
- ConcretePrototype 클래스: Prototype 인터페이스를 구체적으로 구현하고, 객체의 복제를 수행하는 메서드를 제공합니다.
- Client: 복제를 요청하고, 새로운 객체를 생성하는 역할을 합니다.
// Prototype 인터페이스
public interface Prototype {
Prototype clone();
}
// ConcretePrototype 클래스
public class ConcretePrototype implements Prototype {
private String property;
public ConcretePrototype(String property) {
this.property = property;
}
public Prototype clone() {
return new ConcretePrototype(this.property);
}
public void setProperty(String property) {
this.property = property;
}
public String getProperty() {
return property;
}
}
// Client 클래스
public class Client {
public static void main(String[] args) {
// 기존 객체 생성
ConcretePrototype prototype = new ConcretePrototype("Original");
// 객체 복제
ConcretePrototype clonedObject = (ConcretePrototype) prototype.clone();
// 복제된 객체의 속성 변경
clonedObject.setProperty("Cloned");
System.out.println("Original Object: " + prototype.getProperty());
System.out.println("Cloned Object: " + clonedObject.getProperty());
}
}
위의 예제 코드에서는 Prototype
인터페이스를 정의하고, ConcretePrototype
클래스가 이를 구현합니다. ConcretePrototype
클래스는 clone()
메서드를 구현하여 자신을 복제할 수 있습니다. Client
클래스에서는 기존 객체를 생성하고 복제한 객체를 생성한 뒤, 복제된 객체의 속성을 변경하여 결과를 출력합니다.
Prototype 패턴 장점
- 객체의 복제를 통해 객체 생성 비용을 줄일 수 있습니다. 기존 객체를 복제하여 필요한 부분만 변경하여 새로운 객체를 생성할 수 있습니다.
- 객체 생성 과정을 단순화할 수 있습니다. 복잡한 초기화 과정을 거치지 않고, 기존 객체의 상태를 복제하여 새로운 객체를 생성할 수 있습니다.
Prototype 패턴 사용 시 주의할 점
- 객체의 복제 과정에서 얕은 복사와 깊은 복사를 구분해야 합니다. 얕은 복사는 참조된 객체의 주소만 복사하므로 원본과 복사본이 동일한 객체를 참조할 수 있습니다. 깊은 복사는 참조된 객체를 새로 생성하여 복사하므로 원본과 복사본이 독립적인 객체를 참조합니다.
- 객체가 복잡한 상태를 가지고 있을 때, 모든 상태를 정확하게 복제해야 합니다. 복제를 수행하는 메서드를 구현할 때 신경써야 합니다.
- Prototype 패턴은 객체의 구조적인 변경을 동적으로 처리하기 어렵습니다. 객체의 구조가 변경되었을 때, 모든 복제 관련 코드를 수정해야 할 수도 있습니다.
아래는 객체의 복제를 잘못 구현하여 Prototype 패턴을 적절히 사용하지 못한 예제 코드입니다:
// Prototype 인터페이스
public interface Prototype {
Prototype clone();
}
// ConcretePrototype 클래스
public class ConcretePrototype implements Prototype {
private int[] array;
public ConcretePrototype(int[] array) {
this.array = array;
}
public Prototype clone() {
return new ConcretePrototype(this.array); // 잘못된 복제 방식
}
public void setArrayValue(int index, int value) {
array[index] = value;
}
public int[] getArray() {
return array;
}
}
// Client 클래스
public class Client {
public static void main(String[] args) {
// 기존 객체 생성
int[] originalArray = {1, 2, 3};
ConcretePrototype prototype = new ConcretePrototype(originalArray);
// 객체 복제
ConcretePrototype clonedObject = (ConcretePrototype) prototype.clone();
// 복제된 객체의 배열 값 변경
clonedObject.setArrayValue(0, 99);
System.out.println("Original Array: " + Arrays.toString(prototype.getArray()));
System.out.println("Cloned Array: " + Arrays.toString(clonedObject.getArray()));
}
}
위의 예제 코드에서는 ConcretePrototype
클래스의 clone()
메서드를 구현할 때, 배열을 얕은 복사하여 복제하고 있습니다. 이 경우, 원본 객체와 복제 객체가 같은 배열 객체를 참조하게 됩니다. 따라서 setArrayValue()
메서드를 통해 복제된 객체의 배열 값을 변경하면 원본 객체의 배열 값도 변경되는 문제가 발생합니다.
이러한 경우, 올바른 복제 방식은 깊은 복사를 수행하여 복제된 객체와 원본 객체가 서로 독립적인 배열 객체를 참조하도록 해야 합니다. 예를 들어, 배열을 새로 생성하고 배열 요소를 복사하는 방식으로 복제를 수행해야 합니다.
Prototype 패턴과 함께 사용되는 패턴
- 추상 팩토리(Abstract Factory) 패턴: 추상 팩토리 패턴을 사용하여 Prototype 인스턴스를 관리하고, 이를 기반으로 새로운 객체를 생성할 수 있습니다.
- 빌더(Builder) 패턴: 빌더 패턴을 사용하여 Prototype객체를 복제하고, 추가적인 구성 작업을 수행할 수 있습니다.
- 단일체(Singleton) 패턴: 단일체 패턴을 사용하여 Prototype 인스턴스를 공유하고, 필요에 따라 복제하여 새로운 객체를 생성할 수 있습니다.