拷贝构造和原型模式有什么区别?
1. 情景与意图
在我们日常写代码的时候,有一段代码两三百行,在网络上的某处。我们是应该敲出来呢?还是Ctrl C + Ctrl V呢?肯定是第二个啊。这个例子不太好。再比如,某员工每天需要给三个领导一份纸质的日报,员工不会每次都手打三份,而是写一份,然后直接打印三份。这个打印的操作就是稳定的,每天不同的日报就是变化的。
在日常开发中,会面临着复杂对象的创建工作,该对象确经常发生变化,但是却有一致稳定的接口。我们能否提供一个方法让我们依赖这个稳定的接口,从而隔离变化的对象呢?——原型模式。
2. 原型模式
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。类似于拷贝构造的操作。
当学习这个模式的时候,心里肯定会有一个疑问,就是这个和拷贝构造有什么区别?这个在后面进行解释。
3. 克隆动物
我们先通过代码,来认识一下原型模式。
我们有一个抽象的动物类。
class DPAbstractClonedAnimal {
protected:
std::string _name;
public:
virtual void AnimalCry() = 0;
// 可以纯虚也可以不纯虚
virtual DPAbstractClonedAnimal* clone() = 0;
};
来实现几个具体的动物:
class DPClonedSheep : public DPAbstractClonedAnimal {
public:
DPClonedSheep(std::string name);
// 对于原型模式,我们需要对拷贝构造函数严格的实现,因为原型模式,其实就是调用的拷贝构造
DPClonedSheep(const DPClonedSheep& sheep);
virtual void AnimalCry();
virtual DPAbstractClonedAnimal* clone();
};
class DPClonedCattle : public DPAbstractClonedAnimal {
public:
DPClonedCattle(std::string name);
DPClonedCattle(const DPClonedCattle& cattle);
virtual void AnimalCry();
virtual DPAbstractClonedAnimal* clone();
};
实现克隆羊,克隆牛其实同理:
DPClonedSheep::DPClonedSheep(std::string name) {
_name = name;
}
DPClonedSheep::DPClonedSheep(const DPClonedSheep& sheep) {
// 拷贝构造简单实现
_name = std::string("克隆羊") + sheep._name;
}
void DPClonedSheep::AnimalCry() {
std::cout << _name << "咩咩咩" << std::endl;
}
DPAbstractClonedAnimal* DPClonedSheep::clone() {
// 其实调用的还是拷贝构造
return new DPClonedSheep(*this);
}
使用一下:
int main() {
// 对于动物类,我们应该用工厂模式创建对象,不依赖具体的类
DPAbstractClonedAnimal* sheepOne = new DPClonedSheep("喜羊羊");
sheepOne->AnimalCry();
DPAbstractClonedAnimal* sheepTwo = sheepOne->clone();
sheepTwo->AnimalCry();
DPAbstractClonedAnimal* cattleOne = new DPClonedCattle("大角牛");
cattleOne->AnimalCry();
DPAbstractClonedAnimal* cattleTwo = cattleOne->clone();
cattleTwo->AnimalCry();
return 0;
}
4. 总结
拷贝构造和原型模式有什么区别?
设计模式一直在强调的事情就是依赖抽象,控制变化。如果我们在这里直接使用拷贝构造,那么一定是需要使用一个具体的类,使用具体得类就产生了依赖,在面对变化的对象的时候就不能满足。
如果说我们一个对象的创建非常的复杂,而对象有经常发生变化,这个时候可以使用原型模式。
原型模式源代码:【原型模式代码C++源码】