四,设计模式-原型模式
目的
原型模式的产生是为了解决一个问题,即复制对象时对被复制对象所属类的依赖。当需要复制一个对象时,需要遍历对象中的所有成员并进行复制,但存在一些问题:某些成员对象可能是私有的无法访问。同时要复制某个对象,那就必须知道对象所属的类型,这就造成复制这个操作依赖了某个类型。
原型模式是一种创建型设计模式, 为所有支持克隆的对象声明了一个通用接口, 该接口能够克隆对象, 同时又无需将代码和对象所属类耦合,即通过对象进行复制,代替子类的构造。
实现方式
原型模式的实现方式是将克隆的操作委托给被克隆的对象。方法是定义一个包含名字为clone()的接口,所有支持克隆的类型实现该方法,支持克隆的对象即为原型,具体如下:
创建原型接口, 并在其中声明克隆方法。
原则上原型类必须另行定义一个以该类对象为参数的构造函数,构造函数必须复制参数对象中的所有成员变量值到新建实体中。 如果需要修改子类, 则必须调用父类构造函数, 让父类复制其私有成员变量值。在代码实现中使用标准库的make_unique方法,具体的拷贝操作交由该方法进行。
克隆方法的实现:每个类都必须显式重写克隆方法,否则, 克隆方法可能会生成父类的对象。这里使用智能指针以及标准库的make_unique方法。
代码如下:
#include <iostream>
#include <memory>class Prototype {
public:virtual std::unique_ptr<Prototype> clone() const = 0;virtual void print() const = 0;virtual ~Prototype() = default;
};class ConcretePrototypeA : public Prototype {
private:int value;
public:ConcretePrototypeA(int v) : value(v) {}std::unique_ptr<Prototype> clone() const override {return std::make_unique<ConcretePrototypeA>(*this);}void print() const override {std::cout << "ConcretePrototypeA with value: " << value << std::endl;}
};class ConcretePrototypeB : public Prototype {
private:std::string name;
public:ConcretePrototypeB(const std::string& n) : name(n) {}std::unique_ptr<Prototype> clone() const override {return std::make_unique<ConcretePrototypeB>(*this);}void print() const override {std::cout << "ConcretePrototypeB with name: " << name << std::endl;}
};void client(const Prototype& prototype) {auto cloned = prototype.clone();cloned->print();
}int main() {ConcretePrototypeA prototypeA(42);ConcretePrototypeB prototypeB("PrototypeB");std::cout << "Cloning Prototype A:\n";client(prototypeA);std::cout << "\nCloning Prototype B:\n";client(prototypeB);return 0;
}
补充
其实对于这个原型模式的实现,我自己理解“从对象直接拷贝”,更像是对内存块的直接复制(类似memcpy),但如果是这样,那么对包含虚函数、指针等数据和操作的类对象直接这么干就会有很大问题。而Java的类库中本身实现了原型模式(Cloneable),提供了克隆的接口,后来我查到C++的标准库方法std::make_unique也可以做到同样的事情,虽然和我浅显的理解有些出入,但终究是解决了一些疑问。