-
프로토타입 (Prototype) 패턴디자인 패턴 2022. 6. 29. 22:03
기존의 객체를 응용해서 새로운 인스턴스를 만들때 사용된다.
네트워크를 거치거나 db 를 거쳐서 만들어야 하는 등의 복잡한 과정을 거쳐 인스턴스를 만드는 경우 리소스가 많이 드는데, 이미 만들어진 객체를 가지고 복제를 해서 새로운 인스턴스를 만들면 비용을 줄일 수 있다.
아래 그림과 같이 복제 기능을 갖추고 있는 기존 인스턴스를 프로토타입으로 사용해 새 인스턴스를 만들 수 있다.
백기선 - 디자인패턴 강의 프로토타입 패턴 실습
GithubRepository
public class GithubRepository { private String user; private String name; public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
GithubIssue
public class GithubIssue implements Cloneable{ private int id; private String title; private String url; public String getUrl() { return String.format("https://github.com/%s/%s/issues/%d", repository.getUser(), repository.getName(), this.getId()); } public void setUrl(String url) { this.url = url; } private GithubRepository repository; public GithubIssue(GithubRepository repository) { this.repository = repository; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public GithubRepository getRepository() { return repository; } public void setRepository(GithubRepository repository) { this.repository = repository; } @Override // 얕은복사 protected Object clone() throws CloneNotSupportedException { return super.clone(); } // 깊은복사 protected Object clone2() throws CloneNotSupportedException { GithubRepository repository = new GithubRepository(); repository.setUser(this.repository.getUser()); repository.setName(this.repository.getName()); GithubIssue githubIssue = new GithubIssue(repository); githubIssue.setId(this.id); githubIssue.setTitle(this.title); return githubIssue; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GithubIssue that = (GithubIssue) o; return id == that.id && Objects.equals(title, that.title) && Objects.equals( url, that.url) && Objects.equals(repository, that.repository); } @Override public int hashCode() { return Objects.hash(id, title, url, repository); } }
App
public class App { public static void main(String[] args) throws CloneNotSupportedException { GithubRepository repository = new GithubRepository(); repository.setUser("whiteship"); repository.setName("live-study"); GithubIssue githubIssue = new GithubIssue(repository); githubIssue.setId(1); githubIssue.setTitle("1주차 과체: JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가"); String url = githubIssue.getUrl(); System.out.println(url); GithubIssue clone = (GithubIssue) githubIssue.clone(); System.out.println(clone != githubIssue); //새로운 인스턴스이기 때문에 다르다. System.out.println(clone.equals(githubIssue)); //세팅되어져 있는 데이터는 같을 수 있다. System.out.println(clone.getClass() == githubIssue.getClass()); System.out.println(clone.getRepository() == githubIssue.getRepository()); //얕은복사 System.out.println(clone.getUrl()); } } //결과 https://github.com/whiteship/live-study/issues/1 true true true true https://github.com/whiteship/live-study/issues/1
위 코드처럼 자바는 인스턴스를 복제해주는 기본적인 메커니즘을 제공하는데 이 기능을 사용하여 프로토타입 패턴을 구현할 수 있다.
이를 사용하려면 Object 클래스의 clone 메서드를 사용해야한다. 하지만 protected 로 설정되어 있어 이 부분을 사용가능하도록 명시적으로 바꿔줘야하고 프로토타입 패턴을 적용할 클래스에 Cloneable 를 상속받아 Clone 메서드를 재정의하면 된다.
이때 생성자 파라미터로 참조객체가 넘어가면 해당 참조객체는 새로운 객체일까? 아니면 같은 주소값을 가진 객체일까?
public GithubIssue(GithubRepository repository) { this.repository = repository; }
clone 메서드를 수정하지 않고 ide 에서 만들어준대로 재정의하였다면 얕은복사 즉, 같은 주소값을 가진 객체가 사용된다.
// 얕은복사 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }
반대로 새로운 주소값을 가진 객체를 사용하고 싶다면 (깊은복사) 아래와 같이 clone 메서드를 구현하자.
// 깊은복사 protected Object clone() throws CloneNotSupportedException { GithubRepository repository = new GithubRepository(); repository.setUser(this.repository.getUser()); repository.setName(this.repository.getName()); GithubIssue githubIssue = new GithubIssue(repository); githubIssue.setId(this.id); githubIssue.setTitle(this.title); return githubIssue; }
장점과 단점
장점
- 복잡한 객체를 만드는 과정을 숨길 수 있다.
- 기존 객체를 복제하는 과정이 새 인스턴스를 만드는 것보다 비용(시간 또는 메모리)적인 면에서 효율적일 수도 있다.
- 추상적인 타입을 리턴할 수 있다.
단점
- 복제한 객체를 만드는 과정 자체가 복잡할 수 있다. (특히, 순환 참조가 있는 경우)
참고
인프런 - 백기선, 디자인패턴 강의
'디자인 패턴' 카테고리의 다른 글
브릿지 패턴 (Bridge Pattern) (0) 2022.07.12 어댑터 패턴 (Adapter Pattern) (0) 2022.07.05 추상 팩토리 (Abstract factory) 패턴 (0) 2022.06.19 팩토리 메소드 패턴 (Factory Method Pattern) (0) 2022.06.14 싱글톤 패턴 (Singleton Pattern) (0) 2022.06.04