原型模式
原型模式
原型(Prototype)模式是一种创建型模式。原型模式通过(原型对象)克隆出对个一模一样的对象。实际上,该模式与其说是一种设计模式,不如说是一种创建对象的方法(对象克隆),尤其是创建给定类的对象(实例)过程很复杂(例如,要设置许多成员变量的值)时,适用这种设计模式就比较合适。
引入"原型"(Prototype)模式的定义:用原型实例指定创建对象的类型,并且通过复制这些原型创建新的对象。简单来说,就是通过克隆来创建新的对象实例。
主要组成部分
- 原型接口 (Prototype Interface) :定义一个克隆方法,通常是
clone()
,用于复制当前对象的实例。 - 具体原型类 (Concrete Prototype):实现原型接口,提供具体的克隆实现。每个具体原型类都可以被克隆以创建新对象。
- 客户端 (Client):使用原型对象来创建新对象。客户端通过调用原型的克隆方法来获得新对象,而不是直接使用构造函数。
原型模式的使用步骤
-
定义原型接口
创建一个包含克隆方法的抽象类
Monster
。cpp//怪物父类 class Monster { public: //构造函数 Monster(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {} virtual ~Monster() {} //做父类时析构函数应该为虚函数 public: virtual Monster* clone() = 0; //具体的实现在子类中进行 protected: //可能被子类访问的成员,用protected修饰 //怪物属性 int m_life; //生命值 int m_magic; //魔法值 int m_attack; //攻击力 };
-
实现具体原型类
创建具体的怪物类,继承自
Monster
,并实现clone()
方法。cpp//亡灵类怪物 class M_Undead :public Monster { public: //构造函数 M_Undead(int life, int magic, int attack) :Monster(life, magic, attack) { cout << "一只亡灵类怪物来到了这个世界" << endl; } public: //拷贝构造函数 M_Undead(const M_Undead& tmpobj) :Monster(tmpobj) { cout << "调用了M_Undead::M_Undead(const M_Undead& tmpobj)拷贝构造函数创建了一只亡灵类怪物" << endl; } virtual Monster* clone() { return new M_Undead(*this); //触发拷贝构造函数的调用来创建亡灵类怪物 } //其他代码略.... }; //元素类怪物 class M_Element :public Monster { public: //构造函数 M_Element(int life, int magic, int attack) :Monster(life, magic, attack) { cout << "一只元素类怪物来到了这个世界" << endl; } public: //拷贝构造函数 M_Element(const M_Element& tmpobj) :Monster(tmpobj) //初始化列表中注意对父类子对象的初始化 { cout << "调用了M_Element::M_Element(const M_Element& tmpobj)拷贝构造函数创建了一只元素类怪物" << endl; } virtual Monster* clone() { return new M_Element(*this); } //其他代码略.... }; //机械类怪物 class M_Mechanic :public Monster { public: //构造函数 M_Mechanic(int life, int magic, int attack) :Monster(life, magic, attack) { cout << "一只机械类怪物来到了这个世界" << endl; } public: //拷贝构造函数 M_Mechanic(const M_Mechanic& tmpobj) :Monster(tmpobj) { cout << "调用了M_Mechanic::M_Mechanic(const M_Mechanic& tmpobj)拷贝构造函数创建了一只机械类怪物" << endl; } virtual Monster* clone() { return new M_Mechanic(*this); } //其他代码略.... };
-
使用客户端
在客户端代码中,创建原型对象并使用克隆方法生成新对象。
cppint main() { // 创建原型对象 Monster* undead = new M_Undead(100, 50, 20); Monster* element = new M_Element(80, 60, 25); Monster* mechanic = new M_Mechanic(120, 30, 30); // 克隆对象 Monster* clonedUndead = undead->clone(); Monster* clonedElement = element->clone(); Monster* clonedMechanic = mechanic->clone(); // 使用克隆对象... // 例如,可以在这里对克隆对象进行操作或输出属性 // 清理内存 delete undead; delete element; delete mechanic; delete clonedUndead; delete clonedElement; delete clonedMechanic; return 0; }
原型模式的 UML 图
原型模式 UML 图解析
- 类关系 :
- Prototype (原型类):这是一个抽象类,定义了克隆接口(通常是
clone()
方法)。在 UML 图中,对应于Monster
类。 - ConcretePrototype (具体原型类):这些是实现了 Prototype 接口的具体类,负责实现克隆逻辑。图中的
M_Undead
、M_Element
和M_Mechanic
是具体原型类。
- Prototype (原型类):这是一个抽象类,定义了克隆接口(通常是
- 稳定与变化 :
- 稳定部分 :Prototype 类(如
Monster
)通常保持不变,定义了基本的属性和克隆接口。 - 变化部分 :ConcretePrototype 类(如
M_Undead
、M_Element
等)是变化的部分,可以根据需求添加新的具体类。
- 稳定部分 :Prototype 类(如
- 信息隐藏 :
- 客户端代码与 Prototype 接口交互,而不需要了解具体的实现细节。这种设计实现了信息隐藏,使得系统更加灵活。
- 扩展性 :
- 新的具体原型类可以通过实现 Prototype 接口来扩展,而不需要修改现有的代码结构,符合开闭原则。
优点和缺点
优点:
- 简化对象创建:通过克隆现有对象,避免了复杂的构造过程,尤其是当对象需要大量初始化参数时。
- 提高灵活性:客户端不需要了解具体的对象创建逻辑,只需使用原型进行克隆,增强了代码的灵活性和可维护性。
- 支持动态配置:可以在运行时决定克隆哪种类型的对象,适应性强。
缺点:
- 性能开销:克隆对象可能导致较大的内存开销,特别是在对象较大或复杂时。
- 深拷贝与浅拷贝:需要明确区分深拷贝和浅拷贝,错误的实现可能导致共享状态问题。
- 增加复杂性:需要实现克隆方法,可能会增加代码的复杂性,特别是在有多个子类时。
适用场景
- 对象创建过程复杂:当对象的构造过程非常复杂,涉及多个参数初始化时,使用原型模式可以简化这一过程。
- 需要大量相似对象:当需要创建大量相似对象时,使用克隆原型可以提高效率。
- 运行时动态创建:当对象类型在运行时动态决定时,原型模式可以提供灵活的解决方案。
- 避免构造函数的重复调用:在需要频繁创建相似对象的场景中,原型模式可以避免重复调用构造函数,提高性能。
总结
原型模式通过定义 Prototype 和 ConcretePrototype 的关系,使得对象的克隆过程更加灵活和高效。客户端只需依赖于 Prototype 接口,能够动态创建新的对象实例,增强了系统的可扩展性和可维护性。