의도
원형이 되는 객체를 만들어 놓고 필요할때 이를 복사해서 새로운 객체를 생성한다.
기존에 존재하는 객체와 비슷하거나 일부만 변경된 객체가 필요한 경우에 사용된다. new를 통해서 계속 새로운 인스턴스를 생성할 수 있지만, 비슷한 어트리뷰트를 가지는 경우, clone을 통해서 인스턴스를 복제하는 것이 더 간편하다.
ex) DB에서 필요한 정보를 가져오는 경우, 거의 비슷한 객체가 반복적으로 사용된다면 요청이 올때마다 DB에 붙는거보다 한번만 붙어서 정보를 가져오고 이후부터는 해당 정보를 가지고 있는 객체를 복사해서 사용하는 것이 효율적이다.
프로토타입 패턴을 사용할 때 필수적인 내용은 프로토타입 인터페이스의 구현체 서브클래스에서 Clone() 연산을 구현해야 한다는 점이다.
활용성
이미 존재하는 인스턴스의 정보를 복제해서 새로운 인스턴스를 생성하기 때문에 Product의 생성, 복합, 표현 방법에 독립적인 신규 Product를 생성하고자 할 때 사용한다.
- 인스턴스화할 클래스를 런타임에 지정할 때 (동적 로딩)
- 클래스의 인스턴스들이 서로 다른 상태 조합 중에 어느 하나의 값을 가질 때, 해당 상태에 대한 프로토타입을 미리 생성해두고 나중에는 이를 복제해서 사용하고 싶을 때
구조
- Client : Prototype인터페이스에 복제를 요청해서 새로운 객체를 전달 받는다
- Prototype : 자신을 복제하는데 필요한 인터페이스를 정의한다.
- ConcretePrototypeA, B : clone()을 구현하는 객체
장단점
- (+) 복잡한 객체를 생성하는 과정을 clone() 메서드에 숨길 수 있다
- (+) 새로운 인스턴스르 생성하는것보다 비용적인 측면에서 효율적일 수 있다
- (+) 서브클래스의 수를 줄일 수 있다.
- (+) 팩토리 메서드처럼 Creator클래스를 구현하는 ConcreteCreator가 제품 수 만큼 나와야 하는 것이 아니라, 프로토타입의 복제를 통해서 create액션을 처리하므로 Creator를 따로 상속받아서 구현하는 서브 클래스가 필요없다. Creator객체가 이미 프로토타입의 역할을 하기 때문이다.
- (-) 복잡한 객체는 만드는 과정 자체가 복잡할 수 있다 (특히, 순환참조의 경우)
구현
- 자바에서는 Object 클래스에서 제공하는 clone() 메서드를 사용할 수 있음
- Cloneable interface를 구현한 (clone() 메서드 구현) ConcreateProtoType 객체
public class GithubIssue implements Cloneable {
private Integer id;
private String title;
private GithubRepository repository;
public GithubIssue(GithubRepository repository){
this.repository = repository;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl(){
return String.format("httsp://github.com/%s/%s/issues/%s", this.repository.getUser(), this.repository.getName(), this.id);
}
@Override
public GithubIssue clone() {
try {
// 기본적인 clone()은 shallow copy지원을 하기 때문에 deep copy를 원하면 여기에 추가로 구현
GithubIssue clone = (GithubIssue) super.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
- Client (main)
public class PrototypeMain {
public static void main(String[] args) {
GithubRepository repository = new GithubRepository();
repository.setUser("user1");
repository.setName("dev-repo");
GithubIssue issue1 = new GithubIssue(repository);
issue1.setId(1);
issue1.setTitle("issue1 title here");
// issue1
System.out.println(issue1.getUrl());
// issue1을 복제한 객체
GithubIssue clone = (GithubIssue) issue1.clone();
System.out.println(clone.getUrl());
}
}
'디자인패턴' 카테고리의 다른 글
[GoF의 디자인 패턴] 빌더 패턴 (0) | 2022.03.06 |
---|---|
[GoF의 디자인 패턴] 추상 팩토리 패턴 (0) | 2022.03.04 |
[GoF의 디자인 패턴] 싱글톤 패턴 (0) | 2022.03.01 |
[GoF의 디자인 패턴] 어댑터 패턴 (0) | 2021.11.21 |
[GoF의 디자인 패턴] 전략 패턴 (0) | 2021.11.18 |