원형(Prototype) 패턴은 프로토타입이 되는 인스턴스를 사용해 생성할 객체의 종류를 명시하고 만들어진 객체를 복사하여 이로부터 새로운 객체를 생성하는 패턴이다.
사용자는 프로토타입이 되는 객체에 자기 자신의 복제를 요청하여 새로운 객체를 얻는다. 이 패턴은 특히 같은 클래스들의 인스턴스들을 수많이 만들 필요가 있을 때 유용하다. 또한 이런 인스턴스들이 가지고 있는 상태 값이 각양각색일 때, 미리 기본 값을 가지고 있는 객체를 만들어두었다가 이 객체로부터 복제해서 사용하면 더 편리할 때도 있다.
특정 클래스의 인스턴스를 직접 생성하는 비용 대비 복제해서 사용하는 비용이 더 적을 때 이 패턴을 사용함으로써 성능을 향상시킬 수도 있다.
이 패턴을 활용하면 런타임시에 동적으로 사용할 인스턴스의 종류를 변경할 수도 있으며, 특정 클래스의 동작까지도 변경할 수 있다.
public class Client {
private Prototype prototype; // Prototype 의 인스턴스를 참조한다.
public void setPrototype(Prototype prototype) {
this.prototype = prototype; // 동적으로 Prototype을 바꿀 수 있다.
}
public operation() {
...
p = prototype.clone();
p.something(); // 동적으로 바뀌는 Prototype의 인스턴스에 따라 Client의 operation 메서드의 동작을 바꿀 수 있다.
...
}
}
위의 코드와 같이 이 패턴을 사용하면, 특정 Prototype 객체를 지정함으로써 Client의 동작 방식도 변경할 수 있다. 이 특징은 전략 패턴과 유사하다.
팩토리 메서드 패턴에서는 특정 구체 클래스의 인스턴스가 필요할 때마다 팩토리 메서드를 구현하는 서브 클래스를 별도로 정의해야 했지만, 원형 패턴에서는 새로운 클래스의 인스턴스가 필요하다고 해서 별도로 다른 클래스(팩토리 클래스)를 정의할 필요는 없다. 그냥 필요한 클래스를 정의하고 프로토타입이 되는 인스턴스를 생성 후, 복제해달라고 요청만 하면 된다.
이 패턴을 사용하기 위해서는 복제하는 연산(clone)을 정확하게, 제대로 구현해야 한다. 특히 얕은 복사 / 깊은 복사 문제에 대해 세심히 고려해야 한다.
인스턴스를 프로토타입 인스턴스로부터 복제 후, 자기가 원하는 값으로 필드 값을 설정하고 싶으면 이를 위한 메서드를 추가로 정의해야 한다. 복제를 담당하는 메서드(clone)이 매개변수를 받아, 복제 후 적절한 값으로 설정하는 것은 일반적인 상황에서는 부적절하다. 원형 클래스마다 필요한 매개변수의 개수가 천차만별이기 때문이다. 복제를 담당하는 메서드는 일관성이 있어야 한다.
구체 서브 클래스 팩토리를 사용한다는 점에서 추상 팩토리 패턴과는 경쟁적인 관계이지만, 함께 사용할 수도 있다. 추상 팩토리 패턴에서 팩토리 클래스가 프로토타입 인스턴스를 갖고 있다가 객체 생성 요청이 들어오면 복제하여 객체를 반환하도록 구현할 수도 있다.