일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 로버트마틴
- 백준
- 람다 칼큘러스
- 디자인패턴
- Pattern
- JDBC
- javscript
- lambda calculus
- 스택
- 큐
- 프로그래머스
- Java
- DesignPattern
- Eclipse
- functional programming
- Spring
- Collection
- 함수형 프로그래밍
- Network
- Python
- solid
- exception
- Collections
- 파이썬
- 겨울카카오인턴
- tcp
- Rails
- JavaScript
- 자바
- design-pattern
- Today
- Total
개발자 노트
Prototype Pattern 본문
출처
프로토타입 패턴이란?
- 생성 디자인 패턴
- 생성할 object의 클래스에 의존하지 않고 이미 생성된 object를 통해 동일한 object를 생성할 수 있는 디자인 패턴입니다.
![](https://refactoring.guru/images/patterns/content/prototype/prototype-2x.png)
프로토타입?
앞으로 설명드릴 프로토타입이란 용어와 일상생활에서 쓰이는 용어와 차이점이 있습니다. 제가 이 패턴을 처음 접했을 때 가장 헷갈려서 개념을 받아들이기 어려웠기 때문에 차이점을 먼저 짚어보겠습니다.
일상생활에서 쓰이는 프로토타입 용어
- 이 프로토타입은 대량생산 전 성능을 검증하고 개선하기 위해 핵심기능만 넣어서 제작한 모델을 프로토타입이라 부릅니다.
- 또는 게임의 본격적인 개발에 앞서 재미요소나 구현 가능성을 검증하기 위해 제작하는 시제품을 의미하지요.
아마 이 두 뜻이 일반적으로 알고 있는 내용이라 생각이 듭니다. 그렇기 때문에 이 정의에 맞추어 프로토타입 패턴을 받아들이려고 하면 어려움이 뒤따를 수 밖에 없습니다. 프로토타입 패턴에서 프로토타입은 다음과 같습니다.
프로토타입 패턴에서 프로토타입 용어
자기 자신을 복제하는 기능을 지원하는 객체를 프로토타입이라고 부릅니다. (자기 자신을 어떻게 복제하는지는 아래에서 자세히 설명드리겠습니다.)
An object that supports cloning is called a prototype.
자기 자신을 생성한다는 면에서 큰 차이점이 있습니다. 따라서 이 프로토타입은 오히려 자기복제하는 아메바와 비슷한 의미하는 것이지요.
![](https://refactoring.guru/images/patterns/content/prototype/prototype-comic-3-en-2x.png)
그래도 두 용어의 어원은 '원초적 형태'라는 의미를 가지고 있긴 합니다.
상황
- object를 똑같이 복제하려 합니다.
방법 - object의 클래스를 이용
- object의 클래스에서 새로운 object를 생성합니다.
- 복제하고 싶은 object의 모든 필드를 그대로 새로운 object로 카피하여 옮깁니다.
문제점
- 필드가 private로 선언된 경우 object의 field값에 접근못할 수 있습니다.
- 이 때문에 런타임 중 object를 복제하지 못합니다.
- concrete class에 의존하게 됩니다.
- concrete class를 이용하여 생성하기 때문입니다.
- object의 인터페이스만 알고 있는 경우 생성할 수 없습니다.
- 다른 interface를 통해서 객체를 받은 경우 concrete class를 모를 수 있습니다.
해결방법
복제하는 책임을 class에서 복제하려는 object에게 넘깁니다!
concrete class를 찾아 생성할 필요가 없어집니다.
object 자신은 자신의 field에 접근할 수 있기 때문에 private로 선언된 문제도 해결할 수 있습니다.
복제하려는 objects들의 interface를 선언합니다.
- 생성한 object를 concrete class로 받을 필요가 없어집니다.
- 보통 이 interface는 자기 자신을 복제하는 clone() 메서드를 선언합니다.
구조
1. Basic implementation
![](https://refactoring.guru/images/patterns/diagrams/prototype/structure-indexed-2x.png)
1. Prototype
프로토타입을 만드려는 객체 클래스들의 interface입니다. (다시 한번 프로토타입을 말하자면 자가 복제 기능을 가진 객체!!)
이 인터페이스를 통해서 clone를 상속받고, 프로토타입 객체들을 이 공통의 타입으로 받을 수 있게 되지요. (다형성)
public interface Prototype {
public Prototype clone();
}
2. ConcretePrototype
프로토타입의 인터페이스를 구현합니다. 따라서 이 클래스는 clone()메서드를 가지고 있습니다.
public class Circle implements Prototype {
private int x;
private int y;
public Circle(Circle prototype) {
this.x = prototype.x;
this.y = prototype.y;
}
public Circle(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public Prototype clone() {
return new Circle(this);
}
}
clone()
그리고 clone()을 다음과 같은 방식으로 구현합니다.
생성자에 자신의 타입을 가진 객체를 넘길 수 있도록 합니다.
초기화를 위해 파라미터로 받는 생성자를 만들어줍니다.
- 복제하기 위해 자신의 객체를 넣어주어야 하는데 이 부분이 없으면 자신의 객체를 만들 수가 없기 때문입니다.
sub-class
- super(prototype)를 이용하여 상위 class에 값을 전달해줍니다.
public class RedCircle extends Circle {
private String color;
public RedCircle(RedCircle prototype) {
super(prototype);
this.color = prototype.color;
}
//javascript와 비슷한 방식
public RedCircle(Circle circle, String color) {
super(circle);
this.color = color;
}
public RedCircle(int x, int y, String color) {
super(x, y);
this.color = color;
}
public Prototype clone() {
return new RedCircle(this);
}
}
3. Client
Prototype interface를 통해 객체를 생성할 수 있습니다.
public class Client { Prototype circle = new Circle(3, 5); Prototype redCircle1 = new RedCircle(3, 5, "red"); Prototype redCircle2 = new RedCircle((Circle) circle, "red"); Prototype newCircle = circle.clone(); Prototype newRedCircle = redCircle1.clone(); }
2.Prototype registry implementation
프로토타입들을 콜렉터를 통해 관리할 수 있도록 구현 합니다. 따라서 클라이언트에게 어떻게 생성하는지는 숨길 수 있고, 클라이언트는 이 저장소를 통해 값을 가져올 수 있습니다.
여기서 클라이언트는 값을 제공해주고 빼내오는 클래스로 1. 초기화하여 레지스트리에 값을 저장하려는 클래스, 2. 레지스트리에 접근하여 값을 가져오려는 클래스 두 가지로 봐도 될 것 같습니다.
![](https://refactoring.guru/images/patterns/diagrams/prototype/structure-prototype-cache-indexed-2x.png)
비판 - 자바에서 프로토타입 패턴 문제점
프로토타입을 구현한 객체들의 다른 내용을 사용하려면 캐스팅을 해주어야 합니다. 이때는 다른 메서드도 추가할 것이니 abstract를 적용해보겠습니다.
Shape [prototype class]
abstract class Shape {
private int x;
private int y;
public Shape(int x, int y) {
this.x = x;
this.y = y;
}
public Shape(Shape shape) {
this.x = shape.x;
this.y = shape.y;
}
public abstract Shape clone();
}
이 프로토타입은 도형이라는 것만 복제할 것이기 때문에 필드에 int x, int y를 선언해주었습니다.
Circle
public class Circle extends Shape{
private int radius;
public Circle(int x, int y, int radius) {
super(x, y);
this.radius = radius;
}
public Circle(Circle circle) {
super(circle);
this.radius = circle.radius;
}
public int getRadius() {
return radius;
}
@Override
public Shape clone() {
return new Circle(this);
}
}
도형이라는 프로토타입을 상속받아 Circle이라는 속성을 부여해주는 radius 필드를 추가하였습니다. 그리고 이 radius에 접근하려면 getRadius() 메서드를 선언주어야겠지요.
Client
public class Client {
public static void main(String[] args) {
Circle circle = new Circle(3,5,110);
//에러
Shape circle1 = circle.clone();
circle1.getRadius();
//캐스팅
Circle circle2 = (Circle) circle.clone();
circle2.getRadius();
/**
* 3자에게 주입받아 타입을 모른 채 사용할 수 있도록 하려 함. 그래서 Shape라는 프로토타입 인터페이스를
* 선언해주었으나, Circle이라는 class 의미를 부여한 radius에 접근하는 getRadius()를 사용하기 위해서는
* object의 타입을 확인해주고 Circle로 캐스팅해주어야 했음.
*/
}
}
주석에 달아놓은 것처럼, 3자에게 주입받아 타입을 모른 채 사용할 수 있어야 하는데, getRadius에 접근하려면 캐스팅이 필요합니다. 즉, 객체를 이용하려면 타입을 알아야만 한다는 것이지요.
캐스팅은 좋지 못한 방법이라 생각하기 때문에 자바에는 그리 적절하진 않은 것 같습니다.
따라서 타입이 동적으로 변하는 파이썬이나 자바스크립트에 더 어울리는 방법이라 보이네요.
다른 패턴과 관계
- Factory method에서 복잡해지면 Prototype적용 가능합니다.
- 생성 책임 분리
- Abstract Factory가 factory methods의 모음이라 말할 수 있는데, 이 factory method의 생성하는 부분을 prototype 패턴으로 대체할 수 있습니다.
'디자인패턴 > Creational Design Pattern' 카테고리의 다른 글
Singleton Pattern (0) | 2020.10.11 |
---|---|
Builder Pattern (0) | 2020.10.11 |
Abstract Factory (0) | 2020.10.11 |
Factory Method Pattern (0) | 2020.10.11 |