본문 바로가기

Develop/DESIGN PATTERNS

[GoF Design Pattern] 프로토타입 패턴 (Prototype pattern)

1. 프로토타입 패턴이란?

기존 인스턴스를 복제하여 새로운 인스턴스를 만드는 패턴이다.

인스턴스를 생성하는 기존 방법의 비용이 큰 경우 (ex. DB 또는 네트워크 작업) 매번 새로운 인스턴스를 만들기 부담이 될 수 있다.

따라서 기존 인스턴스를 복제해서 새로운 인스턴스를 만들고, 원하는 값만 변경하여 사용할 수가 있다.

 

이때 기존 인스턴스는 복제 기능을 갖추고 있어야 한다. (자바에서는 clone() 메서드 기본 제공)

2. 구현 방법

 

마찬가지로 다음과 같은 구조를 가져갈 수 있다.

GithubIssue clone = (GithubIssue) githubIssue.clone();
repository.setUser("Keesun");

System.out.println(clone != githubIssue); // true
System.out.println(clone.equals(githubIssue)); // false
System.out.println(clone.getClass() == githubIssue.getClass()); // true
System.out.println(clone.getRepository() == githubIssue.getRepository()); // false

위 예제는 GithubRepository 클래스의 인스턴스를 생성하는 데 많은 비용이 든다고 가정한다.

따라서 기존의 githubIssue 인스턴스를 복제하여, 해당 인스턴스가 가진 repository의 필드값을 커스텀하여 사용한다.

자바는 얕은 복사(Shallow Copy)를 기본으로 하기 때문에 clone()을 통해 복제하더라도 서로 다른 참조값을 가진다.

3. 장/단점

1) 장점

복잡한 객체를 만드는 과정을 clone() 메서드 안에 숨길 수 있다. 이때 생성 비용이 크다면 아주 효율적이다.

또한 clone() 메서드가 반환하는 타입이 반드시 해당 클래스의 타입과 같지 않아도 된다. 즉, 조금 더 추상화된 타입을 반환함으로써 객체를 보다 유연하게 만들 수 있다는 장점이 있다.

2) 단점

인스턴스를 복제하는 과정 자체가 복잡해질 수 있다. (예를 들어 해당 객체가 순환 참조를 가진다면?)

4. 자바 컬렉션에서는?

자바 컬렉션에서도 clone() 메서드를 사용한다.

List<Student> students = new ArrayList<>();
students.add(keesun);
students.add(whiteship);

List<Student> clone = new ArrayList<>(students);

다만 우리가 일반적으로 필드를 선언할 때 추상화를 위해 구체적인 클래스보다는 List<T>와 같은 타입을 주로 사용한다.

이때 List<T>는 Clonable 인터페이스를 구현하지 않았기 때문에 clone() 메서드를 사용할 수 없다.

 

해당 인터페이스는 ArrayList<T>와 같은 구체적인 타입이 구현하고 있다.

따라서 컬렉션에서는 clone() 메서드를 사용하기보다, new ArrayList<>(students); 와 같이 생성자를 활용해서 복제를 하게 된다.

 

ModelMapper modelMapper = new ModelMapper();
GithubIssueData githubIssueData = modelMapper.map(githubIssue, GithubIssueData.class);

또한 ModelMapper 클래스를 활용해서 인스턴스를 복제할 수도 있다.

ModelMapper는 내부적으로 자바 리플렉션을 활용하고 있다.