본문 바로가기

개발/Java

[Java] Design-Pattern : ProtoType

프로토타입 패턴은 싱글톤과 같이 생성 패턴(Creathinal Patterns) 중 하나이다.

기존 인스턴스를 프로토타입으로 사용하여 복제해 새로운 인스턴스를 생성하는 디자인 패턴이다.

 

최상위 객체인 Object 객체의 clone() 메서드를 활용하여 객체를 복제할 수 있다.

실제 clone() 메서드를 오버라이드 하고자 한다면, Cloneable 인터페이스를 구현하면 된다.


아래와 같은 클래스가 있다고 가정하자.

public class GithubIssue {
    private int id;
    private String title;
    private GithubRepository repository;
    
    public GithubIssue(GithubRepository repository) {
        this.repository = repository;
    }
    //getter & setter
}

 

일반적으로 해당 클래스를 인스턴스로 생성하고자 한다면, 아래와 같이 될 것이다.

public class App {
    public static void main(String[] args) {
        GithubRepository repository = new GithubRepository();
        GithubIssue githubIssue = new GithubIssue(repository);
        githubIssue.setId(1);
        githubIssue.setTitle("1주차 과제: JVM");
    }
}

Clone을 사용하여 객체를 복제할 수 있다.

public class GithubIssue implements Cloneable {
    private int id;
    private String title;
    private GithubRepository repository;
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

 

이 경우, 원본 객체와 clone()으로 복제한 객체는 새로운 객체이지만,

내부 필드의 객체는 동일한 주소값을 가르키고 있는 문제가 있다.(얕은 복사 - Shallow copy)

public class App {
    public static void main(String[] args) throws CloneNotSupportedException {
        GithubRepository repository = new GithubRepository();

        GithubIssue githubIssue = new GithubIssue(repository);
        githubIssue.setId(1);
        githubIssue.setTitle("1주차 과제: JVM");

        GithubIssue clone = (GithubIssue) githubIssue.clone();
        
        System.out.println(clone == githubIssue); // false
        System.out.println(clone.getClass() == githubIssue.getClass()); // true
        System.out.println(clone.getRepository() == githubIssue.getRepository()); // true
    }
}

 

이 경우 오버라이딩 한 clone() 메서드를 재정의 하면 된다.

결국 깊은복사(Deep copy)가 되도록 모든 필드에 대해 재정의 해주어야 한다.

이러한 로직은, 구현부에서 정의하던 객체 생성 부분을 내부로 가져와 숨길 수 있다.

public class GithubIssue implements Cloneable {

    //중략
    
    @Override
    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;
    }
}
System.out.println(clone.getRepository() == githubIssue.getRepository()); // false

디자인 패턴을 학습하기 위해 정리하였지만,

사실 effective java에서는 앞서 언급한 얕은 복사 문제로 인해, clone() 메서드의 사용에 대해 주의를 요한다.


참고 : 인프런, 백기선, 코딩으로 학습하는 GoF의 디자인 패턴(https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/)

 

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

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

www.inflearn.com