티스토리 뷰

디자인 패턴

05. 프로토타입 패턴

jin-park 2022. 2. 10. 16:58

1.  프로토타입 패턴?

  • 기존 인스턴스를 복제하여 새로운 인스턴스를 생성하는 방법

2.  목적

  • 아래 PrototypeClient에서 GithubIssue를 새로 생성할때 처음부터 객체를 만드는것이 아니라 기존에 있는 인스턴스를 복제하여 바뀐부분만 수정하여 만들고자 함.
public class GithubIssue {
 
    private final GithubRepository githubRepository;
    private Integer id;
    private String title;
 
    public GithubIssue(GithubRepository githubRepository) {
        this.githubRepository = githubRepository;
    }
 
    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 "url"; }
}
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; }
}
public class PrototypeClient {
 
    public static void main(String[] args) {
        GithubRepository githubRepository = new GithubRepository();
        githubRepository.setUser("jin-park");
        githubRepository.setName("gof design pattern study");
 
        GithubIssue githubIssue = new GithubIssue(githubRepository);
        githubIssue.setId(1);
        githubIssue.setTitle("1강: 싱글톤 패턴");
 
        String url = githubIssue.getUrl();
        System.out.println("url - " + url);
    }
}

3.  패턴 설명

  • 기존 인스턴스를 복제하기 위해서는 Object 클래스의 clone 메서드를 사용한다.
    • 기존 인스턴스 != 클론 인스턴스 → cloen()은 인스턴스를 생성하므로 동일 하지 않음.
    • 기존 인스턴스.equals(클론 인스턴스) → clone()은 내부 내용은 같기때문에 동등함.

3.1.  clone 사용 방법

  1. Cloneable 인터페이스를 구현한다.
  2. clone() 메서드를 오버라이드 한다.
  3. equals와 hashCode 메서드를 오버라이드한다.
public class GithubIssue implements Cloneable {
   ....
   ....
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
 
   @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        GithubIssue that = (GithubIssue) o;
        return Objects.equals(githubRepository, that.githubRepository) && Objects
            .equals(id, that.id) && Objects.equals(title, that.title);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(githubRepository, id, title);
    }
}
public class PrototypeClient {
 
    public static void main(String[] args) throws CloneNotSupportedException {
        GithubRepository githubRepository = new GithubRepository();
        githubRepository.setUser("jin.bak");
        githubRepository.setName("gof design pattern study");
 
        GithubIssue githubIssue = new GithubIssue(githubRepository);
        githubIssue.setId(1);
        githubIssue.setTitle("1강: 싱글톤 패턴");
 
        GithubIssue clone = (GithubIssue) githubIssue.clone();
        System.out.println("githubIssue == clone -> " + (githubIssue != clone)); // true
        System.out.println("githubIssue.equals(clone) -> " + githubIssue.equals(clone)); // true
        System.out.println("githubIssue.getClass() == clone.getClass() -> " + (githubIssue.getClass() == clone.getClass())); // true
    }
}

3.2.  clone 메서드 특징 - 얕은 복사

3.2.1.  깊은 복사 vs 얕은 복사

  • 얕은 복사 - 객체를 복사시에 주소(레퍼런스)만 복사해 오는 방식, 한 쪽에서 수정이 발생되면 다른쪽에도 영향을 끼쳐 같아짐.
  • 깊은 복사 - 주소값을 참조하는 것이 아닌, 새로운 메모리 공간에 값을 복사하는 방식.

3.2.2.  얕은 복사로 인한 문제점

  • 위에 githubIssue.clone()를 실행하면 내부에 참조하고 있는 githubRepository는 얕은 복사로 같은 인스턴스를 가지고있게된다.
  • 만약 githubRepository를 수정하게 되면 githubIssue와 clone된 인스턴스 내부의 githubRepository내용이 모두 바뀐다.
public class PrototypeClient {
 
    public static void main(String[] args) throws CloneNotSupportedException {
       ....
       System.out.println(clone.getGithubRepository() == githubIssue.getGithubRepository()); // true
    }
}

3.3.  clone 메서드 - 깊은 복사로 변경

ublic class GithubIssue implements Cloneable {
   ....
   ....
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        GithubRepository githubRepository = new GithubRepository();
        githubRepository.setUser(this.githubRepository.getUser());
        githubRepository.setName(this.githubRepository.getName());
 
        GithubIssue githubIssue = new GithubIssue(githubRepository);
        githubIssue.setId(this.id);
        githubIssue.setTitle(this.title);
 
        return githubIssue;
    }
 
  ....
  ....
}

4.  장점 및 단점

4.1.  장점

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

4.2.  단점

  • 객체 생성이 복잡한 경우 clone 메서드 구현 또한 복잡해질 수 있다. (특히 순환참조의 경우)

위글은 인프런의 코딩으로 학습하는 GoF의 디자인 패턴강의를 정리하였습니다.

백기선님의 수락으로 정리하였으며 더 자세한 내용은 강의를 수강하시기 바랍니다.

https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard

 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com

 

'디자인 패턴' 카테고리의 다른 글

07. 브릿지 패턴  (0) 2022.02.10
06. 어댑터 패턴  (0) 2022.02.10
04. 빌더패턴  (0) 2022.02.10
03. 추상 팩토리 패턴  (0) 2022.02.10
02. 팩토리 메소드 패턴  (0) 2022.02.10
댓글