반응형
[디자인패턴] 원형 패턴( Prototype Pattern)
의도 : 원형이 되는(Prototype) 인스턴스를 사용하여 생성할 객체 종류를 명시하고 견본을 복사해 새 객체를 생성한다.
본문
원형 패턴은 제품의 생성, 복합 표현 방법에 독립적인 제품을 만들고자 할 때 사용한다.
활용성
제품의 생성, 복합, 포현 방법에 독립적인 제품을 만들고자 할 때 사용한다.
- 인스턴스화할 클래스를 런타임에 지정하는 경우
- 제품 클래스 계통과 병렬적으로 만드는 팩토리 클래스를 피하고 싶은 경우
- 클래스의 인스턴스들이 서로 다른 상태 조합 중 어느 하나인 경우
UML
Prototype : 자신을 복제하는 데 필요한 인터페이스를 정의한다.
ConcretePrototype : 자신을 복제하는 연산을 구현한다.
Client : 원형에 자기 자신의 복제를 요청하여 새로운 객체를 생성한다.
결과
- 런타임에 새로운 제품을 추가 및 삭제할 수 있다.
- 값들을 다양화하여 새로운 객체를 명세한다.
- 새 클래스 생성 대신에 객체의 변수 값을 지정하는 식으로 새로운 행동을 정의할 수 있다.
- 구조를 다양화하여 새로운 객체를 명세한다.
- ex) 사용자 정의 구조를 등록하고 이를 clone 하는 방식
- 서브클래스의 수를 줄일 수 있다.
- 객체를 생성하는 것이 아닌 원형을 복제하는 식으로 새로운 상속 계층을 줄일 수 있다.
- 동적으로 클래스에 따라 응용프로그램을 설정할 수 있다.
구현
c++ 같은 정적 언어에서 유용하다.
- 원형 관리자(Prototype Manager) 사용
- 원형관리자는 특정 키에 부합되는 원형을 저장하고, 찾아서 반환하거나 삭제하는 기능을 담당하는 저장소이다.
- 런타임에 레지스트리를 변경하거나 검색할 수 있으며 이를 통해 코드 외에도 이러한 관리 기능을 이용하거나 확장할 수 있다.
- 각 프로토타입을 특정 키 값에 매칭시키는 방식을 이용
- Clone() 연산 구현
- 원형 패턴에서 Clone을 얕은 복사와 깊은 복사 중 어떤 방식을 사용할 지 생각할 필요가 있음.
- 얕은 복사 : 특정 객체가 저장된 주소를 복사하는 것. B가 A를 얕은 복사한 경우 B를 수정한다면 A도 수정되게 됨.
- 깊은 복사 : 특정 객체의 필드 값들을 복사하는 것. 얕은 복사와 다르게 B가 A를 수정하더라도 A는 수정되지 않음.
- Save, Load 연산이 정이되어 있으면 Save 연산으로 메모리 버퍼에 저장하고 Load로 메모리 버퍼에 저장된 객체를 복사하는 식으로 구현이 가능하다.
- 원형 패턴에서 Clone을 얕은 복사와 깊은 복사 중 어떤 방식을 사용할 지 생각할 필요가 있음.
- Clone 초기화
- 사용자가 내부 필드를 자신이 선택한 값으로 초기화 하길 원할 수 있음. 이런 경우 객체 복제 이후 이를 수행하는 Initialize() 등의 이름을 가진 메서드를 사용자에게 제공해야함.
예시
Clone을 통해 제품을 복제하여 제품을 생성한다.
UML
Display.java
더보기
public class Display {
public Display() {
_producer = "기본";
}
public Display(Display other) {
_producer = other._producer;
}
public void testDisplay() {
System.out.println(_producer + "디스플레이 테스트");
}
public Display Clone() {
return new Display(this);
}
public void Initialize(String producer) {
_producer = producer;
}
protected String _producer;
}
InnerDevice.java
더보기
public class InnerDevice {
public InnerDevice() {
_producer = "기본";
}
public InnerDevice(InnerDevice other) {
_producer = other._producer;
}
public void testKeyboardInput() {
System.out.println(_producer + "키보드 입력 테스트");
}
public void testMicrophoneInput() {
System.out.println(_producer + "마이크 입력 테스트");
}
public void Initialize(String producer) {
_producer = producer;
}
public InnerDevice Clone() {
return new InnerDevice(this);
}
private String _producer;
}
Phone.java
더보기
public class Phone {
private Display _display;
private InnerDevice _innerDevice;
public Phone() {
_display = new Display();
_innerDevice = new InnerDevice();
}
public Phone(Phone other) {
_display = other._display;
_innerDevice = other._innerDevice;
}
public void Initialize(InnerDevice i, Display d) {
_display = d;
_innerDevice = i;
}
public Display getDisplay() {
return _display;
}
public InnerDevice getInnerDevice() {
return _innerDevice;
}
public Phone Clone() {
return new Phone(this);
}
}
PhonePrototypeFactory.java
더보기
public class PhonePrototypeFactory {
PhonePrototypeFactory(Phone p, InnerDevice i_d, Display d) {
_prototypePhone = p;
_prototypeInnerDevice = i_d;
_prototypeDisplay = d;
}
public Phone makePhone() {
return _prototypePhone.Clone();
}
public InnerDevice makeInnerDevice(String producer) {
InnerDevice innerDevice = _prototypeInnerDevice.Clone();
innerDevice.Initialize(producer);
return innerDevice;
}
public Display makeDisplay(String producer) {
Display display = _prototypeDisplay.Clone();
display.Initialize(producer);
return display;
}
private Phone _prototypePhone;
private InnerDevice _prototypeInnerDevice;
private Display _prototypeDisplay;
}
TestPrototypePattern.java
더보기
public class TestPrototypePattern {
public static void main(String[] args) {
PhonePrototypeFactory phonePrototypeFactory = new PhonePrototypeFactory(
new Phone(),
new InnerDevice(),
new Display()
);
Phone sPhone = CreateSamsungPhone(phonePrototypeFactory);
Phone iPhone = CreateApplePhone(phonePrototypeFactory);
System.out.println("\n- 삼성 -");
sPhone.getDisplay().testDisplay();
sPhone.getInnerDevice().testMicrophoneInput();
sPhone.getInnerDevice().testKeyboardInput();
System.out.println("\n- 애플 -");
iPhone.getDisplay().testDisplay();
iPhone.getInnerDevice().testMicrophoneInput();
iPhone.getInnerDevice().testKeyboardInput();
}
public static Phone CreateSamsungPhone(PhonePrototypeFactory phonePrototypeFactory) {
Phone phone = phonePrototypeFactory.makePhone();
phone.Initialize(
phonePrototypeFactory.makeInnerDevice("삼성"),
phonePrototypeFactory.makeDisplay("삼성")
);
return phone;
}
public static Phone CreateApplePhone(PhonePrototypeFactory phonePrototypeFactory) {
Phone phone = phonePrototypeFactory.makePhone();
phone.Initialize(
phonePrototypeFactory.makeInnerDevice("애플"),
phonePrototypeFactory.makeDisplay("애플")
);
return phone;
}
}
실행결과
마무리
추상 팩토리 패턴과 어떤 면에서 경쟁적인 관계이다. 하지만 함께 사용될 수도 있다. 예를 들어 추상 팩토리 패턴은 원형 집합을 저장하다 필요할 때 복제하여 객체를 반환하도록 사용할 수 있다.
복합체 패턴과 장식자 패턴을 많이 사용해야 하는 설계에서 사용하면 좋을 것이다.
참고
에릭 감마, 리처드 헬름, 랄프 존슨, 존 블리시디스, 『GoF의 디자인 패턴』, 프로텍 미디어, 2015
'공부 > 디자인 패턴' 카테고리의 다른 글
[디자인패턴] 의존성 주입이란? (0) | 2023.03.12 |
---|---|
[디자인패턴] 팩토리 매서드 (Factory Method Pattern) (0) | 2022.04.02 |
[디자인패턴] 빌더 패턴 (Builder Pattern) (0) | 2022.03.25 |
[디자인패턴] 추상 팩토리 패턴 (Abstract Factory Pattern) (0) | 2022.03.23 |