ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프로토타입 (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;
    }

     

     

     

    장점과 단점


    장점

    • 복잡한 객체를 만드는 과정을 숨길 수 있다.
    • 기존 객체를 복제하는 과정이 새 인스턴스를 만드는 것보다 비용(시간 또는 메모리)적인 면에서 효율적일 수도 있다.
    • 추상적인 타입을 리턴할 수 있다.

     

    단점

    • 복제한 객체를 만드는 과정 자체가 복잡할 수 있다. (특히, 순환 참조가 있는 경우)

     

     

     

    참고


    인프런 - 백기선, 디자인패턴 강의

Designed by Tistory.