목차
어떤 상황에서 쓰일까?
DB에서 데이터를 읽어와 인스턴스를 생성해야 하는 경우,
또는 네트워크를 거쳐 인스턴스를 생성해야 하는 경우는 시간도 오래걸리고 리소스도 많이 소모하게된다.
기존에 이미 DB에서 데이터를 가져온 인스턴스가 있다면
해당 인스턴스를 복사하고, 값만 조금 수정해서 사용해도 되지 않을까?!
프로토타입 패턴이란?
기존 인스턴스를 복제하여 새로운 인스턴스를 만드는 패턴이다.
기존의 객체를 응용해서 새로운 인스턴스를 만들 때 활용한다
장점
- 복잡한 객체 만드는 과정을 숨길 수 있다.
- 인스턴스를 만드는 것보다 효율적일 수 있다.
단점
- 순환참조가 있는 경우, 복제과정 자체가 복잡할 수 있다.
구현방법
기존코드
GithubRepository repository = new GithubRepository();
repository.setUser("lotu-us");
repository.setName("study");
GithubIssue issue = new GithubIssue(repository);
issue.setId(1);
issue.setTitle("디자인패턴 : 싱글톤");
String url = issue.getUrl();
System.out.println(url); // http://github.com/lotu-us/study/issues/1
//새로운 URL을 가져오고자하면 아래방법으로 이슈 생성..?
GithubIssue issue2 = new GithubIssue(repository);
issue2.setId(2);
issue2.setTitle("디자인패턴 : 빌더");
깃허브 이슈 URL을 생성할 때,
repository에 저장된 User와 Name은 동일하고 issue의 id, title만 바뀔텐데
새로운 이슈를 만들때 또 GithubRepository를 의존해서 데이터를 가져와야할까???
변경코드
자바의 Object 객체는 clone메서드를 이미 지원하고 있다!
Cloneable 인터페이스를 구현받아 clone() 메서드를 override받자
public class GithubIssue implements Cloneable {
~~~~
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
GithubRepository repository = new GithubRepository();
repository.setUser("lotu-us");
repository.setName("study");
GithubIssue issue = new GithubIssue(repository);
issue.setId(1);
issue.setTitle("디자인패턴 : 싱글톤");
String url = issue.getUrl();
System.out.println(url); // http://github.com/lotu-us/study/issues/1
GithubIssue cloneIssue = null;
try{
cloneIssue = (GithubIssue) issue.clone();
}catch(CloneNotSupportedException){
e.printStackTrace();
}
System.out.println(cloneIssue.getUrl()); // http://github.com/lotu-us/study/issues/1
주의할 점!
== 과 equals
GithubIssue issue = new GithubIssue(repository);
GithubIssue cloneIssue = issue.clone();
🔥 cloneIssue != issue => true
== 은 주소값을 비교한다.
clone메서드로 새로운 인스턴스를 생성한 것이기에 서로의 주소값 다르다
🔥 cloneIssue.equals(issue) => true
equals 는 값을 비교한다.
clone메서드로 새로운 인스턴스를 생성했어도 서로의 값이 같기때문에 true이다.
shallow copy(얕은 복사) vs deep copy(깊은 복사)
GithubRepository repository = new GithubRepository();
repository.setUser("lotu-us");
GithubIssue issue = new GithubIssue(repository);
issue.setId(1);
System.out.println(issue.getUrl());
// http://github.com/lotu-us/study/issues/1
GithubIssue cloneIssue = (GithubIssue) issue.clone();
//clone한 후 서로 바라보고 있던 repository의 user가 변경되면?!
repository.setUser("GumiBear");
System.out.println( cloneIssue.getRepository() == issue.getRepository() );
//🔥true
//clone과 issue가 바라보는 repository는 서로 같다
System.out.println(cloneIssue.getUrl());
// http://github.com/GumiBear/study/issues/1
자바 Object객체의 clone()은 얕은 복사를 지원한다.
따라서 중간에 서로가 바라보고 있는 repository의 값을 바뀌면 원본과 클론 모두에게 영향을 끼친다!
deep copy를 하려면 @Override한 clone메서드를 재정의하면된다.
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;
}
}
여기서 위 코드처럼 중간에 repository의 값을 변경하면 원본에는 적용되겠지만 클론에는 적용되지 않을 것이다.
repository.setUser("lotu-us");
System.out.println(issue.getUrl());
// http://github.com/lotu-us/study/issues/1
repository.setUser("GumiBear");
System.out.println( cloneIssue.getRepository() == issue.getRepository() );
//🔥false
//clone과 issue가 바라보는 repository가 다르다
System.out.println(issue.getUrl());
//원본 : http://github.com/GumiBear/study/issues/1
System.out.println(cloneIssue.getUrl());
//클론 : http://github.com/lotu-us/study/issues/1
'Computer Science' 카테고리의 다른 글
Bridge Pattern | 브릿지 패턴 (0) | 2022.08.15 |
---|---|
Adapter Pattern | 어댑터패턴 (0) | 2022.08.14 |
Abstract Factory | 추상 팩토리 패턴 (0) | 2022.08.14 |
Factory Method Pattern | 팩토리 메서드 패턴 (0) | 2022.07.29 |
Singleton pattern | 싱글톤패턴 (3) | 2022.07.29 |