# Prototype Pattern
덱스 린지어스(Dex Ringeus)는 사용자들이 회원 등록 양식에 불편함을 느끼는 것을 발견했다. 이것을 좀 더 편리하게 만들어야 한다.
페드로: 등록 양식에 무슨 문제가 있나요?
이브: 사용자들이 입력할 항목들이 너무 많아서 짜증날 정도예요.
페드로: 예를 들면요?
이브: 예를 들면 체중 항목이예요. 여성 사용자들의 90%가 그런 항목을 보면 짜증을 낼 거예요.
페드로: 하지만 이 항목은 우리의 분석 시스템에 중요해요. 이 항목 값에 근거해 음식과 옷을 추천해 주고 있거든요.
이브: 그러면 이 항목을 필수 입력 항목에서 제외하기로 하죠. 이 항목 값이 입력되지 않으면 기본값을 넣어 주고요.
페드로: 60kg이면 적당할까요?
디브: 그런 것 같아요.
페드로: 알았어요. 2분만 기다려 주세요.
두 시간이 흐른 뒤
페드로: 모든 항목이 기본값으로 채워진 등록 양식 프로토타입을 사용해요. 사용자가 양식 작성을 끝냈을 때, 기본값들을 변경하면 돼요.
이브: 좋습니다.
페드로: 여기에 표준 등록 양식이 있어요. clone() 메소드에서는 프로토타입을 사용하고 있고요.
public class RegistrationForm implements Cloneable {
private String name = "Zed";
private String email = "zzzed@gmail.com";
private Date dateOfBirth = new Date(1970, 1, 1);
private int weight = 60;
private Gender gender = Gender.MALE;
private Status status = Status.SINGLE;
private List children = Arrays.asList(new Child(Gender.FEMALE));
private double monthSalary = 1000;
private List favouriteBrands = Arrays.asList("Adidas", "GAP");
// few hundreds more properties
@Override
protected RegistrationForm clone() throws CloneNotSupportedException {
RegistrationForm prototyped = new RegistrationForm();
prototyped.name = name;
prototyped.email = email;
prototyped.dateOfBirth = (Date)dateOfBirth.clone();
prototyped.weight = weight;
prototyped.status = status;
List childrenCopy = new ArrayList();
for (Child c : children) {
childrenCopy.add(c.clone());
}
prototyped.children = childrenCopy;
prototyped.monthSalary = monthSalary;
List brandsCopy = new ArrayList();
for (String s : favouriteBrands) {
brandsCopy.add(s);
}
prototyped.favouriteBrands = brandsCopy;
return prototyped;
}
}
페드로: 사용자를 만들 때마다, clone()을 호출해서 기본값을 바꿉니다.
이브: 끔직하네요! 가변 자료형의 세상에서는 동일한 값의 객체를 새로 생성하려면 clone()이 필요해요. 난점은 깊은 복사를 해야 한다는 것입니다. 단순히 레퍼런스를 복사하면 안되고, 재귀적으로 내부의 객체들을 clone() 해야만 하지요. 그런데 그 객체들 중의 일부에 clone() 메소드가 없으면 어떻게 될까요?
페드로: 그게 바로 문제인데, 이 패턴은 그 문제를 해결해 주죠.
이브: 제가 보기에, 새로운 객체를 추가해 줄 때마다 clone 메소드를 구현해 주어야만 한다면, 그것은 제대로 된 해결책이라고 보기 힘들다고 생각해요.
페드로: 클로저로는 이런 문제를 어떻게 피할 수 있죠?
이브: 클로저는 불변 자료구조를 제공해요. 그것이 전부예요.
페드로: 불변 자료구조로 프로토타입 문제를 어떻게 해결한다는 거죠?
이브: 객체를 변경할 때마다, 새로운 불변 객체를 얻게 되요. 그래서 예전 객체는 변경되지 않지요. 불변 자료형의 세상에서는 프로토타입 패턴이 필요 없어요.
(def registration-prototype
{:name "Zed"
:email "zzzed@gmail.com"
:date-of-birth "1970-01-01"
:weight 60
:gender :male
:status :single
:children [{:gender :female}]
:month-salary 1000
:brands ["Adidas" "GAP"]})
;; return new object
(assoc registration-prototype
:name "Mia Vallace"
:email "tomato@gmail.com"
:weight 52
:gender :female
:month-salary 0)
페드로: 훌륭하네요! 하지만 그런 식으로는 성능에 영향을 미치지 않을까요? 새로운 값을 추가할 때마다 수백만 개의 데이터를 복사하려면 꽤 시간이 걸리지 않나요?
이브: 아니, 그렇지 않아요. 구글에 가서 [[존속 데이터 구조]] (persistent data structures)와 [[구조 공유]] (structural sharing)에 관해 검색해 보세요.
페드로: 고마워요.