原型模式 (Prototype Pattern)
NOTE
原型模式通过复制现有实例来创建新对象,而不是通过实例化类。这种模式在创建对象的成本较高时特别有用。
📖 模式定义
原型模式是一种创建型设计模式,它允许复制已有对象,而无需使代码依赖它们所属的类。该模式将克隆过程委派给被克隆的实际对象。
核心要素
- 原型接口:声明克隆方法的接口
- 具体原型:实现克隆方法的类
- 客户端:通过调用克隆方法来创建新对象
🎯 使用场景
适用情况
- 对象创建成本高:初始化对象需要大量资源或时间
- 避免子类创建:不想创建与产品类层次平行的工厂类层次
- 运行时确定对象:需要在运行时确定要创建的对象类型
- 对象状态变化少:对象的状态变化相对较少
- 配置对象:需要创建具有特定配置的对象副本
不适用情况
- 对象创建简单且成本低
- 对象包含循环引用
- 对象状态经常变化
💡 实现方式
TypeScript 实现
typescript
// 原型接口
interface Cloneable {
clone(): Cloneable;
}
// 具体原型 - 图形基类
abstract class Shape implements Cloneable {
public x: number = 0;
public y: number = 0;
public color: string = '';
constructor(source?: Shape) {
if (source) {
this.x = source.x;
this.y = source.y;
this.color = source.color;
}
}
abstract clone(): Shape;
abstract draw(): void;
}
// 具体原型 - 圆形
class Circle extends Shape {
public radius: number = 0;
constructor(source?: Circle) {
super(source);
if (source) {
this.radius = source.radius;
}
}
clone(): Circle {
return new Circle(this);
}
draw(): void {
console.log(`Drawing Circle at (${this.x}, ${this.y}) with radius ${this.radius} and color ${this.color}`);
}
}
// 具体原型 - 矩形
class Rectangle extends Shape {
public width: number = 0;
public height: number = 0;
constructor(source?: Rectangle) {
super(source);
if (source) {
this.width = source.width;
this.height = source.height;
}
}
clone(): Rectangle {
return new Rectangle(this);
}
draw(): void {
console.log(`Drawing Rectangle at (${this.x}, ${this.y}) with size ${this.width}x${this.height} and color ${this.color}`);
}
}
// 原型管理器
class ShapePrototypeManager {
private prototypes: Map<string, Shape> = new Map();
registerPrototype(name: string, prototype: Shape): void {
this.prototypes.set(name, prototype);
}
createShape(name: string): Shape | null {
const prototype = this.prototypes.get(name);
return prototype ? prototype.clone() : null;
}
listPrototypes(): string[] {
return Array.from(this.prototypes.keys());
}
}
// 使用示例
const manager = new ShapePrototypeManager();
// 创建原型
const redCircle = new Circle();
redCircle.x = 10;
redCircle.y = 20;
redCircle.radius = 15;
redCircle.color = 'red';
const blueRectangle = new Rectangle();
blueRectangle.x = 5;
blueRectangle.y = 10;
blueRectangle.width = 30;
blueRectangle.height = 20;
blueRectangle.color = 'blue';
// 注册原型
manager.registerPrototype('redCircle', redCircle);
manager.registerPrototype('blueRectangle', blueRectangle);
// 克隆对象
const clonedCircle = manager.createShape('redCircle') as Circle;
const clonedRectangle = manager.createShape('blueRectangle') as Rectangle;
// 修改克隆对象
clonedCircle.x = 100;
clonedCircle.color = 'green';
clonedRectangle.width = 50;
// 绘制原型和克隆对象
console.log('Original shapes:');
redCircle.draw();
blueRectangle.draw();
console.log('\nCloned shapes:');
clonedCircle.draw();
clonedRectangle.draw();
深拷贝实现
typescript
// 支持深拷贝的复杂对象
class ComplexObject implements Cloneable {
public id: string;
public data: any;
public children: ComplexObject[] = [];
public metadata: Map<string, any> = new Map();
constructor(id: string, data: any = {}) {
this.id = id;
this.data = { ...data };
}
addChild(child: ComplexObject): void {
this.children.push(child);
}
setMetadata(key: string, value: any): void {
this.metadata.set(key, value);
}
// 深拷贝实现
clone(): ComplexObject {
const cloned = new ComplexObject(this.id, this.data);
// 深拷贝子对象
cloned.children = this.children.map(child => child.clone());
// 深拷贝元数据
cloned.metadata = new Map(this.metadata);
return cloned;
}
// 浅拷贝实现
shallowClone(): ComplexObject {
const cloned = new ComplexObject(this.id, this.data);
cloned.children = [...this.children]; // 浅拷贝数组
cloned.metadata = new Map(this.metadata);
return cloned;
}
toString(): string {
return `ComplexObject(${this.id}) with ${this.children.length} children`;
}
}
// 使用示例
const parent = new ComplexObject('parent', { name: 'Parent Object' });
parent.setMetadata('created', new Date());
const child1 = new ComplexObject('child1', { name: 'Child 1' });
const child2 = new ComplexObject('child2', { name: 'Child 2' });
parent.addChild(child1);
parent.addChild(child2);
// 深拷贝
const deepClone = parent.clone();
deepClone.id = 'parent-clone';
deepClone.children[0].id = 'child1-clone';
console.log('Original:', parent.toString());
console.log('Deep Clone:', deepClone.toString());
console.log('Original child1 ID:', parent.children[0].id);
console.log('Clone child1 ID:', deepClone.children[0].id);
Java 实现
java
// 原型接口
interface Prototype extends Cloneable {
Prototype clone();
}
// 具体原型 - 文档类
class Document implements Prototype {
private String title;
private String content;
private List<String> tags;
private Date createdDate;
public Document(String title, String content) {
this.title = title;
this.content = content;
this.tags = new ArrayList<>();
this.createdDate = new Date();
}
// 拷贝构造函数
public Document(Document source) {
this.title = source.title;
this.content = source.content;
this.tags = new ArrayList<>(source.tags);
this.createdDate = new Date(source.createdDate.getTime());
}
@Override
public Document clone() {
return new Document(this);
}
// Getter和Setter方法
public void setTitle(String title) { this.title = title; }
public String getTitle() { return title; }
public void setContent(String content) { this.content = content; }
public String getContent() { return content; }
public void addTag(String tag) { this.tags.add(tag); }
public List<String> getTags() { return new ArrayList<>(tags); }
public Date getCreatedDate() { return new Date(createdDate.getTime()); }
@Override
public String toString() {
return String.format("Document{title='%s', content='%s', tags=%s, created=%s}",
title, content, tags, createdDate);
}
}
// 原型注册表
class DocumentPrototypeRegistry {
private Map<String, Document> prototypes = new HashMap<>();
public void registerPrototype(String key, Document prototype) {
prototypes.put(key, prototype);
}
public Document createDocument(String key) {
Document prototype = prototypes.get(key);
return prototype != null ? prototype.clone() : null;
}
public Set<String> getAvailableTypes() {
return prototypes.keySet();
}
}
// 使用示例
public class PrototypePatternExample {
public static void main(String[] args) {
DocumentPrototypeRegistry registry = new DocumentPrototypeRegistry();
// 创建原型文档
Document reportTemplate = new Document("Monthly Report", "Report content template");
reportTemplate.addTag("report");
reportTemplate.addTag("monthly");
Document letterTemplate = new Document("Business Letter", "Dear [Name],\n\nContent...\n\nSincerely,");
letterTemplate.addTag("letter");
letterTemplate.addTag("business");
// 注册原型
registry.registerPrototype("report", reportTemplate);
registry.registerPrototype("letter", letterTemplate);
// 使用原型创建新文档
Document newReport = registry.createDocument("report");
newReport.setTitle("Q1 Sales Report");
newReport.setContent("Q1 sales data and analysis...");
Document newLetter = registry.createDocument("letter");
newLetter.setTitle("Welcome Letter");
newLetter.setContent("Dear John,\n\nWelcome to our company!\n\nSincerely,");
System.out.println("Original report template: " + reportTemplate);
System.out.println("New report: " + newReport);
System.out.println("New letter: " + newLetter);
}
}
Python 实现
python
import copy
from abc import ABC, abstractmethod
from typing import Dict, Any, List
from datetime import datetime
# 原型接口
class Prototype(ABC):
@abstractmethod
def clone(self):
pass
# 具体原型 - 游戏角色
class GameCharacter(Prototype):
def __init__(self, name: str, character_class: str):
self.name = name
self.character_class = character_class
self.level = 1
self.health = 100
self.mana = 50
self.equipment = []
self.skills = []
self.stats = {
'strength': 10,
'agility': 10,
'intelligence': 10
}
self.created_at = datetime.now()
def add_equipment(self, item: str):
self.equipment.append(item)
def add_skill(self, skill: str):
self.skills.append(skill)
def level_up(self):
self.level += 1
self.health += 20
self.mana += 10
def clone(self):
# 使用深拷贝确保所有嵌套对象都被复制
return copy.deepcopy(self)
def shallow_clone(self):
# 浅拷贝实现
cloned = GameCharacter(self.name, self.character_class)
cloned.level = self.level
cloned.health = self.health
cloned.mana = self.mana
cloned.equipment = self.equipment.copy() # 浅拷贝列表
cloned.skills = self.skills.copy()
cloned.stats = self.stats.copy() # 浅拷贝字典
return cloned
def __str__(self):
return (f"{self.name} ({self.character_class}) - Level {self.level}, "
f"HP: {self.health}, MP: {self.mana}, "
f"Equipment: {len(self.equipment)} items, "
f"Skills: {len(self.skills)} skills")
# 原型工厂
class CharacterPrototypeFactory:
def __init__(self):
self._prototypes: Dict[str, GameCharacter] = {}
def register_prototype(self, key: str, prototype: GameCharacter):
self._prototypes[key] = prototype
def create_character(self, key: str) -> GameCharacter:
prototype = self._prototypes.get(key)
if prototype:
return prototype.clone()
raise ValueError(f"Prototype '{key}' not found")
def get_available_types(self) -> List[str]:
return list(self._prototypes.keys())
# 预配置的角色模板
def create_character_templates() -> CharacterPrototypeFactory:
factory = CharacterPrototypeFactory()
# 战士模板
warrior = GameCharacter("Warrior Template", "Warrior")
warrior.stats['strength'] = 15
warrior.stats['agility'] = 8
warrior.stats['intelligence'] = 7
warrior.health = 120
warrior.mana = 30
warrior.add_equipment("Iron Sword")
warrior.add_equipment("Leather Armor")
warrior.add_skill("Slash")
warrior.add_skill("Block")
# 法师模板
mage = GameCharacter("Mage Template", "Mage")
mage.stats['strength'] = 6
mage.stats['agility'] = 9
mage.stats['intelligence'] = 15
mage.health = 80
mage.mana = 100
mage.add_equipment("Magic Staff")
mage.add_equipment("Robe")
mage.add_skill("Fireball")
mage.add_skill("Heal")
# 盗贼模板
rogue = GameCharacter("Rogue Template", "Rogue")
rogue.stats['strength'] = 8
rogue.stats['agility'] = 15
rogue.stats['intelligence'] = 7
rogue.health = 90
rogue.mana = 60
rogue.add_equipment("Dagger")
rogue.add_equipment("Leather Armor")
rogue.add_skill("Stealth")
rogue.add_skill("Backstab")
# 注册模板
factory.register_prototype("warrior", warrior)
factory.register_prototype("mage", mage)
factory.register_prototype("rogue", rogue)
return factory
# 使用示例
if __name__ == "__main__":
# 创建角色工厂
factory = create_character_templates()
print("Available character types:", factory.get_available_types())
# 创建新角色
player1 = factory.create_character("warrior")
player1.name = "Conan"
player1.level_up()
player1.add_equipment("Steel Sword")
player2 = factory.create_character("mage")
player2.name = "Gandalf"
player2.level_up()
player2.level_up()
player2.add_skill("Lightning Bolt")
player3 = factory.create_character("rogue")
player3.name = "Robin"
player3.add_equipment("Poison Dagger")
print("\nCreated characters:")
print(player1)
print(player2)
print(player3)
# 验证原型没有被修改
original_warrior = factory.create_character("warrior")
print(f"\nOriginal warrior template: {original_warrior}")
print(f"Modified warrior: {player1}")
⚖️ 优缺点分析
✅ 优点
- 性能优势:避免重复的初始化操作
- 简化对象创建:不需要知道具体类就能创建对象
- 动态配置:可以在运行时添加或删除原型
- 减少子类:避免创建与产品类层次平行的工厂类
- 保持状态:可以保存对象的特定状态
❌ 缺点
- 循环引用问题:深拷贝时可能遇到循环引用
- 克隆复杂对象困难:包含复杂引用的对象难以克隆
- 克隆方法实现复杂:需要仔细实现克隆逻辑
🌟 实际应用案例
1. 配置对象原型
typescript
// 应用配置原型
class ApplicationConfig implements Cloneable {
public database: {
host: string;
port: number;
username: string;
password: string;
database: string;
};
public server: {
port: number;
host: string;
ssl: boolean;
};
public features: {
enableLogging: boolean;
enableCaching: boolean;
enableMetrics: boolean;
};
public environment: string;
constructor() {
this.database = {
host: 'localhost',
port: 5432,
username: '',
password: '',
database: ''
};
this.server = {
port: 3000,
host: '0.0.0.0',
ssl: false
};
this.features = {
enableLogging: true,
enableCaching: false,
enableMetrics: false
};
this.environment = 'development';
}
clone(): ApplicationConfig {
const cloned = new ApplicationConfig();
cloned.database = { ...this.database };
cloned.server = { ...this.server };
cloned.features = { ...this.features };
cloned.environment = this.environment;
return cloned;
}
}
// 配置管理器
class ConfigurationManager {
private templates: Map<string, ApplicationConfig> = new Map();
constructor() {
this.initializeTemplates();
}
private initializeTemplates(): void {
// 开发环境模板
const devConfig = new ApplicationConfig();
devConfig.environment = 'development';
devConfig.database.host = 'localhost';
devConfig.database.database = 'myapp_dev';
devConfig.features.enableLogging = true;
devConfig.features.enableMetrics = true;
// 生产环境模板
const prodConfig = new ApplicationConfig();
prodConfig.environment = 'production';
prodConfig.database.host = 'prod-db.example.com';
prodConfig.database.database = 'myapp_prod';
prodConfig.server.ssl = true;
prodConfig.features.enableLogging = true;
prodConfig.features.enableCaching = true;
prodConfig.features.enableMetrics = true;
// 测试环境模板
const testConfig = new ApplicationConfig();
testConfig.environment = 'test';
testConfig.database.host = 'test-db.example.com';
testConfig.database.database = 'myapp_test';
testConfig.features.enableLogging = false;
this.templates.set('development', devConfig);
this.templates.set('production', prodConfig);
this.templates.set('test', testConfig);
}
getConfig(environment: string): ApplicationConfig {
const template = this.templates.get(environment);
if (!template) {
throw new Error(`Configuration template for '${environment}' not found`);
}
return template.clone();
}
createCustomConfig(baseEnvironment: string, customizations: any): ApplicationConfig {
const config = this.getConfig(baseEnvironment);
// 应用自定义配置
if (customizations.database) {
Object.assign(config.database, customizations.database);
}
if (customizations.server) {
Object.assign(config.server, customizations.server);
}
if (customizations.features) {
Object.assign(config.features, customizations.features);
}
return config;
}
}
// 使用示例
const configManager = new ConfigurationManager();
// 获取标准配置
const devConfig = configManager.getConfig('development');
const prodConfig = configManager.getConfig('production');
// 创建自定义配置
const customConfig = configManager.createCustomConfig('production', {
database: {
host: 'custom-db.example.com',
port: 5433
},
server: {
port: 8080
},
features: {
enableCaching: false
}
});
console.log('Development config:', devConfig);
console.log('Custom production config:', customConfig);
2. 图形编辑器中的图形克隆
typescript
// 图形编辑器中的图形对象
abstract class GraphicElement implements Cloneable {
public id: string;
public x: number = 0;
public y: number = 0;
public rotation: number = 0;
public scale: number = 1;
public visible: boolean = true;
public style: {
fillColor: string;
strokeColor: string;
strokeWidth: number;
opacity: number;
};
constructor(id: string) {
this.id = id;
this.style = {
fillColor: '#ffffff',
strokeColor: '#000000',
strokeWidth: 1,
opacity: 1
};
}
abstract clone(): GraphicElement;
abstract render(): void;
abstract getBounds(): { width: number; height: number };
}
// 文本元素
class TextElement extends GraphicElement {
public text: string;
public fontSize: number;
public fontFamily: string;
constructor(id: string, text: string = '') {
super(id);
this.text = text;
this.fontSize = 14;
this.fontFamily = 'Arial';
}
clone(): TextElement {
const cloned = new TextElement(this.id + '_copy', this.text);
cloned.x = this.x;
cloned.y = this.y;
cloned.rotation = this.rotation;
cloned.scale = this.scale;
cloned.visible = this.visible;
cloned.style = { ...this.style };
cloned.fontSize = this.fontSize;
cloned.fontFamily = this.fontFamily;
return cloned;
}
render(): void {
console.log(`Rendering text "${this.text}" at (${this.x}, ${this.y})`);
}
getBounds(): { width: number; height: number } {
return { width: this.text.length * this.fontSize * 0.6, height: this.fontSize };
}
}
// 图像元素
class ImageElement extends GraphicElement {
public src: string;
public width: number;
public height: number;
constructor(id: string, src: string = '') {
super(id);
this.src = src;
this.width = 100;
this.height = 100;
}
clone(): ImageElement {
const cloned = new ImageElement(this.id + '_copy', this.src);
cloned.x = this.x;
cloned.y = this.y;
cloned.rotation = this.rotation;
cloned.scale = this.scale;
cloned.visible = this.visible;
cloned.style = { ...this.style };
cloned.width = this.width;
cloned.height = this.height;
return cloned;
}
render(): void {
console.log(`Rendering image "${this.src}" at (${this.x}, ${this.y}) size ${this.width}x${this.height}`);
}
getBounds(): { width: number; height: number } {
return { width: this.width, height: this.height };
}
}
// 图形编辑器
class GraphicEditor {
private elements: GraphicElement[] = [];
private clipboard: GraphicElement[] = [];
addElement(element: GraphicElement): void {
this.elements.push(element);
}
selectElements(ids: string[]): GraphicElement[] {
return this.elements.filter(element => ids.includes(element.id));
}
copyElements(ids: string[]): void {
const selectedElements = this.selectElements(ids);
this.clipboard = selectedElements.map(element => element.clone());
console.log(`Copied ${this.clipboard.length} elements to clipboard`);
}
pasteElements(offsetX: number = 10, offsetY: number = 10): void {
const pastedElements = this.clipboard.map(element => {
const cloned = element.clone();
cloned.id = this.generateUniqueId();
cloned.x += offsetX;
cloned.y += offsetY;
return cloned;
});
this.elements.push(...pastedElements);
console.log(`Pasted ${pastedElements.length} elements`);
}
duplicateElements(ids: string[], offsetX: number = 10, offsetY: number = 10): void {
const selectedElements = this.selectElements(ids);
const duplicatedElements = selectedElements.map(element => {
const cloned = element.clone();
cloned.id = this.generateUniqueId();
cloned.x += offsetX;
cloned.y += offsetY;
return cloned;
});
this.elements.push(...duplicatedElements);
console.log(`Duplicated ${duplicatedElements.length} elements`);
}
private generateUniqueId(): string {
return 'element_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
renderAll(): void {
console.log('Rendering all elements:');
this.elements.forEach(element => {
if (element.visible) {
element.render();
}
});
}
getElementCount(): number {
return this.elements.length;
}
}
// 使用示例
const editor = new GraphicEditor();
// 创建一些图形元素
const text1 = new TextElement('text1', 'Hello World');
text1.x = 50;
text1.y = 100;
text1.fontSize = 18;
const image1 = new ImageElement('image1', 'logo.png');
image1.x = 200;
image1.y = 150;
image1.width = 120;
image1.height = 80;
// 添加到编辑器
editor.addElement(text1);
editor.addElement(image1);
console.log(`Initial element count: ${editor.getElementCount()}`);
// 复制和粘贴
editor.copyElements(['text1', 'image1']);
editor.pasteElements(20, 20);
console.log(`After paste element count: ${editor.getElementCount()}`);
// 直接复制
editor.duplicateElements(['text1'], 0, 50);
console.log(`After duplicate element count: ${editor.getElementCount()}`);
// 渲染所有元素
editor.renderAll();
🔄 相关模式
与其他模式的关系
- 工厂方法模式:原型模式可以简化工厂方法的实现
- 抽象工厂模式:可以用原型模式实现抽象工厂
- 组合模式:原型模式常用于复制组合结构
- 装饰器模式:可以克隆装饰后的对象
模式组合
typescript
// 原型 + 工厂方法
abstract class PrototypeFactory {
protected prototypes: Map<string, Cloneable> = new Map();
abstract createPrototype(type: string): Cloneable;
registerPrototype(type: string, prototype: Cloneable): void {
this.prototypes.set(type, prototype);
}
create(type: string): Cloneable {
const prototype = this.prototypes.get(type);
if (!prototype) {
return this.createPrototype(type);
}
return prototype.clone();
}
}
class ShapePrototypeFactory extends PrototypeFactory {
createPrototype(type: string): Cloneable {
switch (type) {
case 'circle':
return new Circle();
case 'rectangle':
return new Rectangle();
default:
throw new Error(`Unknown shape type: ${type}`);
}
}
}
🚀 最佳实践
1. 深拷贝 vs 浅拷贝
typescript
class SmartCloneable {
private cloneStrategy: 'shallow' | 'deep' = 'deep';
setCloneStrategy(strategy: 'shallow' | 'deep'): void {
this.cloneStrategy = strategy;
}
clone(): this {
if (this.cloneStrategy === 'shallow') {
return this.shallowClone();
} else {
return this.deepClone();
}
}
private shallowClone(): this {
return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
}
private deepClone(): this {
return JSON.parse(JSON.stringify(this));
}
}
2. 原型注册表的线程安全实现
typescript
class ThreadSafePrototypeRegistry {
private prototypes: Map<string, Cloneable> = new Map();
private readonly lock = new Map<string, boolean>();
async register(key: string, prototype: Cloneable): Promise<void> {
while (this.lock.get(key)) {
await new Promise(resolve => setTimeout(resolve, 1));
}
this.lock.set(key, true);
try {
this.prototypes.set(key, prototype);
} finally {
this.lock.delete(key);
}
}
async create(key: string): Promise<Cloneable | null> {
const prototype = this.prototypes.get(key);
return prototype ? prototype.clone() : null;
}
}
⚠️ 注意事项
- 循环引用:实现深拷贝时要处理循环引用问题
- 性能考虑:深拷贝可能比创建新对象更耗时
- 克隆方法维护:添加新属性时要记得更新克隆方法
- 不可变对象:考虑是否需要克隆不可变对象
- 资源管理:克隆包含资源的对象时要注意资源管理
📚 总结
原型模式是一个实用的创建型模式,特别适用于创建成本高或配置复杂的对象。它通过克隆现有对象来避免重复的初始化工作,提供了很好的性能优势。
使用建议:
- 当对象的创建成本比较大时使用
- 当需要避免创建与产品类层次平行的工厂类层次时使用
- 当一个系统应该独立于它的产品创建、构成和表示时使用
- 注意深拷贝和浅拷贝的选择
- 合理设计原型注册表来管理原型对象
相关链接: