본문 바로가기
이론/GoF

[생성패턴] 프로토타입패턴

by 혀끄니 2023. 6. 1.
728x90

프로토타입(Prototype)패턴이란?

  • 기존 인스턴스를 복제하여 새로운 인스턴스를 만드는 방법
  • 복제 기능을 갖추고 있는 기존 인스턴스를 프로토타입으로 사용해 새 인스턴스를 만들 수 있다.

프로토타입 사용방법

  • 프로토타입패턴 적용전
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 GithubIssue {

    private int id;

    private String title;

    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 String getUrl() {
        return String.format("https://github.com/%s/%s/issues/%d",
                repository.getUser(),
                repository.getName(),
                this.getId());
    }
}
public class App {

    public static void main(String[] args) {
        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 githubIssue2 = new GithubIssue(repository);
        githubIssue2.setId(2);
        githubIssue2.setTitle("2주차~~");
    }

}
  • 프로토타입(얕은 복사)
  • 자바가 제공하는 clone 기능은 얕은 복사가 기본값
  • 원본의 값이 바뀌면 복사도 같이 변경된다.
// Cloneable을 implement 해준다.
public class GithubIssue implements Cloneable{

    private int id;

    private String title;

    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 String getUrl() {
        return String.format("https://github.com/%s/%s/issues/%d",
                repository.getUser(),
                repository.getName(),
                this.getId());
    }

    @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 id == that.id && Objects.equals(title, that.title) && Objects.equals(repository, that.repository);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, title, repository);
    }
}
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.getUrl());
        
        //true
        System.out.println(clone != githubIssue);
        //true
        System.out.println(clone.equals(githubIssue));
        //true
        System.out.println(clone.getClass() == githubIssue.getClass());
        //true
        System.out.println(clone.getRepository() == githubIssue.getRepository());
    }

}
  • 프로토타입(깊은 복사)
    //일일히 값을 넣어주면 됨
    @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;
    }

장점

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

단점

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

실무에서 쓰이는 방법

public class JavaCollectionExample {

    public static void main(String[] args) {
        Student keesun = new Student("keesun");
        Student whiteship = new Student("whiteship");
        //실무에서는 아래 방법을 사용하지 않음
        //ArrayList<Student> students = new ArrayList<>();
        //실무에서 사용하는 방법
        //1. 추상적인 타입을 사용함(List)
        //   - List에서는 clonable 상속하지 않음
        //   - 대신 ArrayList가 clonable을 상속하고 있음
        List<Student> students = new ArrayList<>();
        students.add(keesun);
        students.add(whiteship);

        //ArrayList<Student> clone = (ArrayList<Student>) students.clone();
        //프로토타입을 사용방법은 아니지만, ArrayList를 사용해서 클론을 만들수 있다.
        List<Student> clone = new ArrayList<>(students);
        System.out.println(clone);
    }
}
public class ModelMapperExample {

    public static void main(String[] args) {
        GithubRepository repository = new GithubRepository();
        repository.setUser("whiteship");
        repository.setName("live-study");

        GithubIssue githubIssue = new GithubIssue(repository);
        githubIssue.setId(1);
        githubIssue.setTitle("1주차 과제: JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.");

        //일일이 데이터를 담는 방식
//        GithubIssueData githubIssueData = new GithubIssueData();
//        githubIssueData.setId(githubIssue.getId());
//        githubIssueData.setRepositoryName(githubIssue.getRepository().getName());
        //ModelMapper를 사용해서 담는 방식
        //동작원리는 리플렉션을 이용해서 동작
        ModelMapper modelMapper = new ModelMapper();
        GithubIssueData githubIssueData = modelMapper.map(githubIssue, GithubIssueData.class);
        System.out.println(githubIssueData);
    }
}
728x90

'이론 > GoF' 카테고리의 다른 글

[구조패턴] 어댑터 패턴  (0) 2023.07.24
[구조패턴] 플라이웨이트 패턴  (0) 2023.07.20
[생성패턴] 빌더패턴  (0) 2023.05.23
[생성패턴] 추상팩토리 패턴  (0) 2023.05.22
[생성패턴] 팩토리 메소드 패턴  (0) 2023.05.22