您的位置:首页 > 其它

阿Sam的设计模式学习笔记---- 原型(Prototype)模式

2007-09-05 15:17 811 查看
1, 功能:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
假如我是公司IT管理员,我要管理公司的电脑资料。公司的电脑的配置都一样的,就是序列号不同。如果公司有200台电脑,我就要输入这200条记录。如果采用Prototype的思想,我就建立一条电脑记录,然后对它克隆199次,每次只是稍微修改一下序列号即可!
2, 基本思想:
声明一个克隆自己的接口(Prototype),我们所需要大量生产的对象的类(ConcretePrototype)继承并实现Prototype,完成克隆自己的工作。客户方(Client)可以通过一个ConcretePrototype的原型克隆新的对象。
3, 适用情况:

当一个系统应该独立于它的产品的创建、构成和表示时,应该使用Prototype;

当要实例化的类是在运行时可指定时;

为了避免创建一个与产品类层次平行的工厂类层次时;

当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

4, 结构:



从结构模型中看出Client依赖于抽象(Prototype),符合“抽象不应依赖于具体实现,具体实现应依赖于抽象”。
5, 举例:
不再举移动通信的例子了,说实话,我也审美疲劳了。不过批量生产的例子太多了,信手拈来:比如我们通常所用的ctrl+c和ctrl+v的操作,就算是一种clone动作。
为了达到由浅入深的学习目的,我的代码例子同样也是由浅入深。
(1) Prototype的C++代码的基本构架:
(2) 再举一个高级一点点的例子。首先声明,以我愚笨的大脑,我是不善原创的,有人用C#写了一个例子(/article/4877188.html),我这里把它“翻译”成C++,不对的地方,敬请各位大虾指正,小弟不甚感激!

// // 某人开着一辆车行驶在某条道路上。
// 其中,某个人(姓名?性别?年龄?),车(牌子?)和公路(路名?长度?类型?)都存在不确定元素。
// 1,定义一个抽象司机的Prototype,具体的男司机和女司机都是其子类。
class Driver
{
public:
Driver(const string iName, const int iAge);
virtual ~Driver(){}
virtual Driver * Clone()=0; // Prototype模式的核心
virtual string drive()=0;
string getName() const{return name;}// 定义成const成员函数
string getGender() const {return gender;}
int getAge() const {return age;}
protected: // 定义为保护类型,使子类可见
string name;
string gender;
int age;
};

Driver::Driver(const string iName, const int iAge) : name(iName), gender(""), age(iAge) {}

class ManDriver : public Driver
{
public:
ManDriver(const string iName, const int iAge);
Driver * Clone();
string drive();
};

ManDriver::ManDriver(const string iName, const int iAge) : Driver(iName, iAge)
{ // gender是从基类继承的,不能在初始化列表中直接初始化。
gender = "male"; // gender在Driver的构造函数中初始化,这里是修改其值
}
Driver * ManDriver::Clone()
{
ManDriver * p = new ManDriver("", 0);
*p = *this;
return p;
}
string ManDriver::drive()
{
return (name.append(" is driving"));
}

class WomanDriver : public Driver
{
public:
WomanDriver(const string iName, const int iAge);
~WomanDriver(){}
Driver *Clone();
string drive();
};
WomanDriver::WomanDriver(const string iName, const int iAge) : Driver(iName, iAge)
{
this->gender = "female";
}
Driver * WomanDriver::Clone()
{
WomanDriver * p = new WomanDriver("", 0);
*p = *this;
return p;
}
string WomanDriver::drive()
{
return (name.append(" is driving"));
}

// 2.定义和实现公路
class Road
{
public:
Road(const string iName, const int iLength);
string getRoadName() const {return RoadName;}
string getType() const {return Type;}
int getLength() const {return RoadLength;}
virtual Road * Clone() = 0;

protected:
string RoadName;
string Type;
int RoadLength;
};
Road::Road(const string iName, const int iLength) : RoadName(iName), RoadLength(iLength), Type("")
{}

class Bituminous : public Road // 柏油马路
{
public:
Bituminous(const string iName, const int iLength);
Road * Clone();
};
Bituminous::Bituminous(const string iName, const int iLength) : Road(iName, iLength)
{
Type = "Bituminous";
}
Road * Bituminous::Clone()
{
Bituminous * p = new Bituminous("", 0);
*p = *this;
return p;
}

class Cement : public Road // 水泥马路
{
public:
Cement(const string iName, const int iLength);
Road * Clone();
};
Cement::Cement(const string iName, const int iLength) : Road(iName, iLength)
{
Type = "Cement";
}
Road * Cement::Clone()
{
Cement * p = new Cement("", 0);
*p = *this;
return p;
}

// 3.定义和实现汽车
class Car
{
public:
Car(const string iOilBox, const string iWheel, const string iBody);
virtual string Run() = 0;
virtual string Stop() = 0;
virtual Car * Clone() = 0;
protected:
string OilBox;
string Wheel;
string Body;
};
Car::Car(const string iOilBox, const string iWheel, const string iBody) : OilBox(iOilBox), Wheel(iWheel), Body(iBody) {}

class BMWCar : public Car
{
public:
BMWCar();
string Run();
string Stop();
Car * Clone();
};
BMWCar::BMWCar() : Car( "BMW's OilBox", "BMW's Wheel", "BMW's Body") {}
string BMWCar::Run()
{
return string("BMW is running");
}
string BMWCar::Stop()
{
return string("BMW is stopped");
}
Car * BMWCar::Clone()
{
BMWCar * p = new BMWCar();
*p = *this;
return p;
}

class BenzCar : public Car
{
public:
BenzCar();
string Run();
string Stop();
Car * Clone();
};
BenzCar::BenzCar() : Car( "BMW's OilBox", "BMW's Wheel", "BMW's Body") {}
string BenzCar::Run()
{
return string("Benz is running");
}
string BenzCar::Stop()
{
return string("Benz is stopped");
}
Car * BenzCar::Clone()
{
BenzCar * p = new BenzCar();
*p = * this;
return p;
}

class FordCar : public Car
{
public:
FordCar();
string Run();
string Stop();
Car * Clone();
private:
string OilBox;
string Wheel;
string Body;
};
FordCar::FordCar() : Car( "BMW's OilBox", "BMW's Wheel", "BMW's Body"){}
string FordCar::Run()
{
return string("Ford is running");
}
string FordCar::Stop()
{
return string("Benz is stopped");
}
Car * FordCar::Clone()
{
FordCar * p = new FordCar();
*p = * this;
return p;
}
// 4.所有前面场景中的参与者定义完毕,下面需要开始建设最后的场景(scenario)
class Scenario
{
public:
void Run(Car * pCar, Road * pRoad, Driver * pDriver);
Car * getCar() const {return m_car;}
Driver * getDriver() const {return m_driver;}
Road * getRoad() const {return m_road;}
private:
Car * m_car;
Driver * m_driver;
Road * m_road;
};
void Scenario::Run(Car * pCar, Road * pRoad, Driver * pDriver)
{
m_car = pCar->Clone();
m_driver = pDriver->Clone();
m_road = pRoad->Clone();
}
// 5.客户现在需要建立一个新场景。
int _tmain(int argc, _TCHAR* argv[])
{
Scenario scene;
scene.Run(new BenzCar(), new Cement("Tibet Road", 10), new ManDriver("Zhang Hu", 27));
cout<<"Car Run: " << scene.getCar()->Run().c_str() << endl;
cout<<"Driver Name "<<scene.getDriver()->drive().c_str()<<endl;
cout<<"Driver Gender "<<scene.getDriver()->getName().c_str()<<endl;
cout<<"Road Name "<<scene.getRoad()->getRoadName().c_str()<<endl;
cout<<"RoadType "<<scene.getRoad()->getType().c_str()<<endl;
cout<<"Car Stop "<<scene.getCar()->Run().c_str()<<endl;
return 0;
}

说明:
A.假如此时又有新的类似场景,也是某人开车在某路上跑,此时我们之需要简单的clone一下原型,稍稍修改即可,比如:
scene.Run(new BMWCar(), new Cement(“Jian She Road”, 20), new WomanDriver(“Vivian”, 26));
B.没有工厂类。在Factory Method中,会对每个具体产品定制一个相同类层次的工厂。
C.类的实例属性变化种类有限。比如Car,只有Benz,BMW和Ford三种不同品牌属性,其余相同;又比如Driver,就只分了男司机和女司机。在这种情况下,“建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些”。我记得以前在VC下用ADO开发的时候就接触到大量的Clone接口方法,当时有点不是太理解它们的作用,现在总算是“找到组织了”!
D.动态配置。对每个Scenario的实例,其角色都是在运行时配置的,因为在Scenario的Run方法中,可见的都只是抽象的Prototype。
E.原型管理器。原型管理器负责创建具体原型类的对象,并记录每一个被创建的对象。在一个系统中原型数目不固定时,需要动态的创建和销毁对象,这时原型管理器非常有用。本例中的scenario就是一个简单的原型管理器。
6, 深拷贝与浅拷贝
在C++中,如果一个对象是复合对象或者包含其他的对象,或者成员中有指向其他对象的指针,那么在克隆过程中,克隆出来的新对象的成员是独立于其他对象的?还是完全从原型中复制过来的?从C++的角度来理解,就是在实现clone方法时,若类中没有定义自己的operator =和拷贝构造函数,C++会为类生成缺省的这两个方法。这时,复制出来的对象的指针成员实际指向的还是原型对象指针成员指向的对象。我们对克隆对象的指针成员所指向的对象进行操作,就可能影响到原型对象。(参考Effective C++ Item11)这就是深拷贝,反之则是浅拷贝。
简单举例来说

class A
{
int a;
char * str;
};

深拷贝情况下,所有克隆对象的str成员都指向原型的str的字符串。浅拷贝时,每个成员str有自己独立的空间,复制了原型中str的内容。
7, 总结:
Prototype和工厂模式一样,创建出新的对象,对客户隐藏了对象的创建工作。但是,与通过一个类进行实例化来构造新对象不同,原型模式通过拷贝一个已有的对象生成新对象。Prototype模式隔离了类对象的使用者和具体类型(易变类)之间的耦合关系,它要求这些“易变类”拥有稳定的接口。

class Prototype // Interface
{
public:
virtual Prototype * Clone() = 0;
};

class ConcretePrototype : public Prototype
{
public:
Prototype * Clone();
};
Prototype * ConcretePrototype::Clone()
{
ConcretePrototype * p = new ConcretePrototype(); // Create a new object.
*p = *this; // Copy the data.
return p;
}

Client Code:
{
ConcretePrototype *obj = new ConcretePrototype(); // 创建原型
Prototype * newObject1 = obj->Clone(); // 利用原型复制
Prototype * newObject2 = obj->Clone(); // 再次利用原型复制
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: