객체를 하나 만드는 데 시간이 오래 걸린다고 가정해보자. DB에서 데이터를 읽어오거나, 복잡한 초기화 과정을 거쳐야 하는 경우다. 같은 구조의 객체가 여러 개 필요하다면 매번 처음부터 만드는 건 낭비다.
이미 만들어진 객체를 찍어내듯 복사하면 되지 않을까. 그게 Prototype 패턴이다.
기존 객체를 복제(clone)해서 새 객체를 만드는 패턴이다. 객체 생성 비용이 클 때, 또는 거의 동일한 객체를 여러 개 만들어야 할 때 유용하다.
Java에서는 Cloneable 인터페이스와 clone() 메서드로 구현한다.
public class GameCharacter implements Cloneable {
private String name;
private int hp;
private int attack;
public GameCharacter(String name, int hp, int attack) {
this.name = name;
this.hp = hp;
this.attack = attack;
}
// 복제 메서드
@Override
public GameCharacter clone() {
try {
return (GameCharacter) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return name + " | HP: " + hp + " | ATK: " + attack;
}
}
// 원본 생성
GameCharacter template = new GameCharacter("전사", 100, 30);
// 복제해서 새 캐릭터 생성
GameCharacter warrior1 = template.clone();
warrior1.setName("전사-1");
GameCharacter warrior2 = template.clone();
warrior2.setName("전사-2");
System.out.println(warrior1); // 전사-1 | HP: 100 | ATK: 30
System.out.println(warrior2); // 전사-2 | HP: 100 | ATK: 30
clone()은 Object에 정의된 메서드다. Cloneable을 구현하지 않으면 CloneNotSupportedException이 발생한다.

super.clone()은 얕은 복사(Shallow Copy) 를 수행한다. 기본 타입 필드는 값이 복사되지만, 참조 타입 필드는 같은 객체를 가리키게 된다.
public class GameCharacter implements Cloneable {
private String name;
private int[] skills; // 참조 타입
@Override
public GameCharacter clone() {
try {
GameCharacter copy = (GameCharacter) super.clone();
// 참조 타입은 따로 복사해야 함 — 깊은 복사
copy.skills = this.skills.clone();
return copy;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
얕은 복사 상태에서 복제본의 skills를 수정하면 원본의 skills도 바뀐다. 서로 독립적인 객체가 되어야 한다면 참조 타입 필드는 직접 복사해야 한다.

미리 만들어둔 원본 객체들을 저장소에 등록해두고, 필요할 때 꺼내 복제하는 방식으로 확장할 수 있다.
public class CharacterRegistry {
private Map<String, GameCharacter> registry = new HashMap<>();
public void register(String key, GameCharacter character) {
registry.put(key, character);
}
public GameCharacter get(String key) {
return registry.get(key).clone();
}
}
CharacterRegistry registry = new CharacterRegistry();
registry.register("warrior", new GameCharacter("전사", 100, 30));
registry.register("mage", new GameCharacter("마법사", 70, 60));
GameCharacter w1 = registry.get("warrior");
GameCharacter w2 = registry.get("warrior");
매번 DB를 조회하거나 복잡한 초기화를 반복하는 대신, 미리 만들어둔 객체를 복사해 쓰는 구조다.
Prototype은 new로 처음부터 만들지 않고, 기존 것을 베껴서 시작하는 패턴이다. 복사 후 달라질 부분만 수정하면 되니, 공통 구조가 많을수록 효율적이다.