// ❌ 나쁜 예: 클래스마다 Factory를 만들어야 하는 코드
public class GraphicEditor {
public Graphic createGraphic(String type) {
if (type.equals("Line")) {
return new Line();
} else if (type.equals("Rectangle")) {
return new Rectangle();
} else if (type.equals("Circle")) {
return new Circle();
}
// 새로운 도형을 추가하려면 코드 수정 필요!
}
}
문제점:
public interface Prototype {
Prototype clone(); // 자신의 복사본 반환
}
왜 Interface인가?
Original Object Clone Object
↓ ↓
[data] [data]
↓ ↓
참조 ──────────────────→ 같은 객체
Original Object Clone Object
↓ ↓
[data] [data]
↓ ↓
참조 → 객체A 참조 → 객체A의 복사본

Client ───────────> Prototype (Interface)
(객체 사용자) (복제 규격 정의)
↑
┌──────────────┴──────────────┐
ConcreteProduct1 ConcreteProduct2
(구체적 복제 구현) (구체적 복제 구현)
- Client는 구체 클래스를 몰라도 됨
- prototype.clone() 호출만으로 새 객체 생성
- 런타임에 프로토타입 교체 가능
// Prototype 인터페이스
public interface Prototype {
Prototype clone();
}
// Java의 Cloneable 사용 (Shallow Copy)
public class Stack implements Cloneable {
private final int maxSize;
private int top;
private Object[] store; // 참조 타입!
public Stack(int size) {
this.maxSize = size;
this.store = new Object[size];
this.top = -1;
}
// Shallow Copy: store 배열은 참조만 복사됨
@Override
public Object clone() throws CloneNotSupportedException {
try {
return super.clone(); // 기본 clone()은 shallow copy
} catch (CloneNotSupportedException e) {
return null;
}
}
public void push(Object item) {
if (top < maxSize - 1) {
store[++top] = item;
}
}
}
// Deep Copy 버전
public class Stack implements Cloneable {
private final int maxSize;
private int top;
private Object[] store;
public Stack(int size) {
this.maxSize = size;
this.store = new Object[size];
this.top = -1;
}
// Deep Copy: store 배열까지 복사
@Override
public Object clone() throws CloneNotSupportedException {
try {
Stack result = (Stack) super.clone();
// 배열도 복사해서 완전히 독립적인 객체 생성
result.store = store.clone();
return result;
} catch (CloneNotSupportedException e) {
return null;
}
}
public void push(Object item) {
if (top < maxSize - 1) {
store[++top] = item;
}
}
}
// Cloneable을 사용하지 않고 직접 구현
public class Stack {
private final int maxSize;
private int top;
private Object[] store;
public Stack(int size) {
this.maxSize = size;
this.store = new Object[size];
this.top = -1;
}
// 직접 복제 메서드 구현
public Stack createClone() {
Stack result = new Stack(maxSize);
result.top = this.top;
// 배열 요소 하나하나 복사
for (int i = 0; i <= top; i++) {
result.store[i] = this.store[i];
}
return result;
}
public void push(Object item) {
if (top < maxSize - 1) {
store[++top] = item;
}
}
}
// Prototype 인터페이스
public interface Graphic extends Cloneable {
Graphic clone();
void draw();
}
// 구체적인 도형들
public class Line implements Graphic {
private int x1, y1, x2, y2;
public Line(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
@Override
public Graphic clone() {
try {
return (Graphic) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public void draw() {
System.out.println("선 그리기: (" + x1 + "," + y1 +
") -> (" + x2 + "," + y2 + ")");
}
}
public class Rectangle implements Graphic {
private int x, y, width, height;
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public Graphic clone() {
try {
return (Graphic) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public void draw() {
System.out.println("사각형 그리기: (" + x + "," + y +
") 크기: " + width + "x" + height);
}
}
// Palette: 프로토타입을 관리하는 클라이언트
public class Palette {
private List<Graphic> prototypes = new ArrayList<>();
// 프로토타입 등록
public void addPrototype(Graphic prototype) {
prototypes.add(prototype);
}
// 인덱스로 프로토타입의 복제본 생성
public Graphic makeGraphic(int index) {
if (index >= 0 && index < prototypes.size()) {
// 프로토타입을 복제하여 반환 (구체 클래스 몰라도 됨!)
return prototypes.get(index).clone();
}
return null;
}
// 런타임에 프로토타입 제거 가능
public void removePrototype(int index) {
if (index >= 0 && index < prototypes.size()) {
prototypes.remove(index);
}
}
}
public class GraphicEditor {
public static void main(String[] args) {
Palette palette = new Palette();
// 초기 프로토타입 등록
palette.addPrototype(new Line(0, 0, 100, 100));
palette.addPrototype(new Rectangle(0, 0, 50, 50));
// 사용자가 Line을 선택하여 새 객체 생성
Graphic newLine1 = palette.makeGraphic(0);
Graphic newLine2 = palette.makeGraphic(0);
// 사용자가 Rectangle을 선택하여 새 객체 생성
Graphic newRect = palette.makeGraphic(1);
// 그리기
newLine1.draw();
newLine2.draw();
newRect.draw();
// 런타임에 새로운 프로토타입 추가 가능!
palette.addPrototype(new Line(50, 50, 150, 150));
Graphic newLine3 = palette.makeGraphic(2);
newLine3.draw();
}
}
선 그리기: (0,0) -> (100,100)
선 그리기: (0,0) -> (100,100)
사각형 그리기: (0,0) 크기: 50x50
선 그리기: (50,50) -> (150,150)
// Prototype을 활용한 MazeFactory
public class MazeFactory {
private Maze prototypeMaze;
private Wall prototypeWall;
private Room prototypeRoom;
private Door prototypeDoor;
// 생성자에서 프로토타입 설정
public MazeFactory(Maze m, Wall w, Room r, Door d) {
this.prototypeMaze = m;
this.prototypeWall = w;
this.prototypeRoom = r;
this.prototypeDoor = d;
}
// 프로토타입을 복제하여 객체 생성
public Wall makeWall() {
return prototypeWall.clone();
}
// 런타임에 프로토타입 변경 가능!
public void setWallPrototype(Wall w) {
this.prototypeWall = w;
}
public Door makeDoor(Room r1, Room r2) {
Door door = prototypeDoor.clone();
door.initialize(r1, r2);
return door;
}
}
| 요소 | 역할 | 특징 |
|---|---|---|
| Prototype | 복제 인터페이스 정의 | clone() 메서드 선언 |
| ConcretePrototype | 실제 복제 구현 | clone()을 구체적으로 구현 |
| Client | 프로토타입 사용 | 구체 클래스를 몰라도 복제 가능 |