您的位置:首页 > 其它

设计模式之三:对象创建系列模式

2018-01-24 16:03 316 查看
前面一章《结构型系列模式》介绍了在程序框架设计中可以用到的几种特定场景下对应的针对性模式:

1. 如针对存在大量实例对象的环境下存在提升性能的需求,则可以采用享元模式,以提炼出使用场景下的不变的部分,以共享来减少实例对象数目;

2. 对遗留的子系统想要复用,但是又不想重构子系统,则可使用外观模式,让一组人构建外观类接口内部实现,另一组人则可以按照事先规定好的接口类进行编写,两组齐头并进;

3. 如果核心数据流和操作流固定,但是存在大量辅助的操作函数,这些附加在数据流上的操作函数的数量和顺序可以自由组装,这时可以采用装饰模式;

4. 如果在某些场景下,库的作者对client想要封装个体和团体的区别,让客户可以一致性地使用单个对象和组合结构,这时则采用组合模式;

5. 如果系统UML架构规模过大过深,此时如果系统存在多角度的视角,则可以分离一个系统的不同部分,然后通过关联关系(依赖、组合、聚合等)将各部组合起来,让抽象和实现两者独立演化;

6. 如果要在当前系统中使用一个第三方库,但是因为当前系统的接口规范已经实现规定好了,这时可采用适配器模式让原本接口不兼容而不能一起工作的那些类一起工作;

7. 如果想要封装某个具体的库对象或其他底层对象,控制不同用户的访问权限,并且再额外添加一些其他的辅助功能如性能监测统计等,这时便可以使用代理模式。

这一章则是围绕面向对象编程中的“对象”二字进行分析。我们知道在面向对象编程中,对象作为主要的信息和功能载体,可以说是面向对象编程的核心,但是在不同场景下对象的创建需求是不一样的,如要求某个类的实例对象全局有且只有一个,如要求封装一个同系列操作(滤波)对外提供统一表征,如client层面要使用的对象其实是有多个底层细粒度子对象组装而成,相对client封装组装过程等等。下面并来看看这些不同对象创建需求场景的对应模式。

目录:

造轮子套路之一 抽象工厂模式

造轮子套路之二 建造者模式

造轮子套路之三 工厂方法模式

造轮子套路之四 原型模式

造轮子套路之五 单例模式

造轮子套路之一: 抽象工厂模式



AbstractFactory.h

#ifndef _ABSTRACTFACTORY_H
#define _ABSTRACTFACTORY_H

#include <iostream>
#include <string>

using namespace std;

class IDepartment
{
int id;
string depname;
public:
virtual void Insert(IDepartment * dp)=0;
virtual IDepartment * GetDepartment(int id)=0;
};

class SqlserverDepartment :public IDepartment
{
public:
void Insert(IDepartment * dp)
{
cout << "在SQL Server 中给Department表增加一条记录。\n";
}

IDepartment * GetDepartment(int id)
{
cout << "在SQL Server 中根据ID得到Department表的一条记录。\n";
return NULL;
}
};

class AccessDepartment :public IDepartment
{
public:
void Insert(IDepartment * dp)
{
cout << "在Access 中给Department表增加一条记录。\n";
}

IDepartment * GetDepartment(int id)
{
cout << "在Access 中根据ID得到Department表的一条记录。\n";
return NULL;
}
};

class IUser
{
public:
virtual void Insert(IUser *) = 0;
virtual IUser * GetUser(int id) = 0;
};

class SqlserverUser :public IUser
{
public:
void Insert(IUser *)
{
cout << "在SQL Server中给User表增加一条记录。\n";
}

IUser * GetUser(int id)
{
cout << "在SQL Server 中根据ID得到User表中的一条记录。\n";
return NULL;
}
};

class AccessUser :public IUser
{
public:
void Insert(IUser *)
{
cout << "在Access给User表增加一条记录。\n";
}

IUser * GetUser(int id)
{
cout << "在Access中根据ID得到User表中的一条记录。\n";
return NULL;
}
};

class IFactory
{
public:
virtual IUser * CreateUser() = 0;
virtual IDepartment * CreateDepartment() = 0;
};

class SqlserverFactory :public IFactory
{
public:
IUser * CreateUser()
{
return new SqlserverUser;
}

IDepartment * CreateDepartment()
{
return new SqlserverDepartment;
}
};

class AccessFactory :public IFactory
{
public:
IUser * CreateUser()
{
return new AccessUser;
}

IDepartment * CreateDepartment()
{
return new AccessDepartment;
}
};

#endif


test.cpp

#include "AbstractFactory.h"

int main()
{
IUser * userI = new SqlserverUser;
IDepartment * dptI = new SqlserverDepartment;

IFactory * factory = new SqlserverFactory;

IUser * user = factory->CreateUser();
//IDepartment * dpt = factory->CreateDepartment();
user->Insert(userI);
user->GetUser(1);

IDepartment * dpt = factory->CreateDepartment();
dpt->Insert(dptI);
dpt->GetDepartment(1);

IDepartment * AdptI = new AccessDepartment;

IFactory * Afactory = new AccessFactory;
IDepartment * IDf=Afactory->CreateDepartment();
IDf->Insert(AdptI);
IDf->GetDepartment(1);

IUser *AuserI = new AccessUser;
IUser * AUf = Afactory->CreateUser();
AUf->Insert(AuserI);
AUf->GetUser(1);
return 0;
}


对象需求场景:解决多个抽象类型产品的同时创建问题,但自身代码过于臃肿,需要搭配策略模式或反射机制使用

使用思想:封装

评分: ★★

造轮子套路之二: 建造者模式



//car属性类
class Car
{
public:
void setEngine(string engine){ m_Engine = engine;}
void setWheel(string wheel){ m_Wheel = wheel;}
void setBody(string body){ m_Body = body;}
void disPlay(){ }
private:
string  m_Engine;
string  m_Wheel;
string  m_Body;
};
//建造者模式抽象类,组装流程
class Assemble{
public:
virtual void assembleEngine(){};
virtual void assembleWheel(){};
virtual void assembleBody(){};
virtual Car* getCar(){ return nullptr;};
};
//a型车组装流程
class AssembleCarA : public Assemble
{
public:
AssembleCarA(){ _carA = new Car();}
virtual void assembleEngine( ){ _carA->setEngine("engineA");};
virtual void assembleWheel( ){ _carA->setWheel("whellA");}
virtual void assembleBody( ){ _carA->setBody("bodyA");};
virtual Car* getCar(){ return _carA; }
private:
Car *_carA;
};
//b型车组装流程
class AssembleCarB : public Assemble
{
public:
AssembleCarB(){ _carB = new Car();}
virtual void assembleEngine( ){ _carB->setEngine("engineB");};
virtual void assembleWheel( ){ _carB->setWheel("wheelB");}
virtual void assembleBody( ){ _carB->setBody("bodyB");};
virtual Car* getCar(){ return _carB; }
private:
Car *_carB;
};
//工厂类
class Director{
public:
Director(Assemble* assemble){ m_assemble = assemble;}
void assembleCar(){
m_assemble->assembleEngine();
m_assemble->assembleWheel();
m_assemble->assembleBody();
}
Car* getCar(){return m_assemble->getCar();}
private:
Assemble* m_assemble;
};
int main()
{
Assemble *m_assemble = new AssembleCarA();
Director *m_Director = new Director(m_assemble);
m_Director->assembleCar();
m_Director->getCar()->disPlay();
return 0;
}


对象需求场景:当需要将一个复杂对象的建造和组装过程隐藏封装,实现高内聚,对外仅提供一个成品,实现弱耦合

使用思想:封装去耦合

评分: ★★

造轮子套路之三: 工厂方法模式



//simple factory model
//与简单工厂模式的区别:简单工厂模式在增加新操作时,需要更改工厂函数,违反了开放与封闭原则
//而工厂方法模式支持扩展,在修改时只需要添加新的操作类和工厂类即可,但客户端也需要作相应的修改
#include <iostream>

using namespace std;

//用一个单独的类来做这个创造实例的过程,即工厂
class Operation{
public:
Operation()
{
number1=0;
number2=0;
result=0;
}

Operation(int one,int two):number1(one),number2(two)
{
result=0;
}

void setOne(int one)
{
number1=one;
}

void setTwo(int two)
{
number2=two;
}

int getOne()
{
return number1;
}

int getTwo()
{
return number2;
}

virtual int getResult()
{//虚接口,需要子类自己实现
return result;
}

protected:
int number1;
int number2;
int result;
};

//四个具体操作的子类,主要用于实现虚函数接口
class AddOperation:public Operation{
public:
virtual int getResult()
{
result=number1+number2;
return result;
}
};

class SubOperation:public Operation{
public:
virtual int getResult()
{
result=number1-number2;
return result;
}
};

class MulOperation:public Operation{
public:
virtual int getResult()
{
result=number1*number2;
return result;
}
};

class DivOperation:public Operation{
public:
virtual int getResult()
{
result=number1/number2;
return result;
}
};

//抽象工厂函数,也是C++版的接口,不想Java等其他语言,存在单独的关键词interface来描述接口类
//C++一般通过抽象类来表示接口类,这也是为什么C++支持多继承,而其他语言支持单继承+多接口实现的原因
class Factory{
public:
virtual Operation* createOper()=0;
};

//四个具体工厂
class AddFactory:public Factory{
Operation* createOper()
{
AddOperation* addOper=new AddOperation();
return addOper;
}
};

class SubFactory:public Factory{
Operation* createOper()
{
SubOperation* subOper=new SubOperation();
return subOper;
}
};

class MulFactory:public Factory{
Operation* createOper()
{
MulOperation* mulOper=new MulOperation();
return mulOper;
}
};

class DivFactory:public Factory{
Operation* createOper()
{
DivOperation* divOper=new DivOperation();
return divOper;
}
};

int main()
{
int number1,number2;
cout<<"please input number1:";
cin>>number1;
cout<<endl;

cout<<"please input number2:";
cin>>number2;
cout<<endl;

Factory* factory=new AddFactory();//创建新工厂,用于生产新的对象
Operation* oper;//具体操作在创建时确定

oper=factory->createOper();
oper->setOne(number1);
oper->setTwo(number2);

cout<<"The Result is:";
cout<<oper->getResult()<<endl;

return 0;
}


对象需求场景:看起来和抽象工厂模式很相似,但是两者规模不一样,抽象工厂同时要管理多个不同类型的产品,而工厂方法则只管理一个类型的多不同系列产品的创建

使用思想:封装去耦合

评分: ★★★

造轮子套路之四: 原型模式



Prototype.h

#ifndef _Prototype_
#define _Prototype_

#include <string>
#include <iostream>

using namespace std;

class AbsGoods{
public:
virtual AbsGoods* Clone() = 0;  //关键,克隆函数
string _mName;
protected:
AbsGoods(const AbsGoods& another){} //拷贝构造函数
~AbsGoods(){} //析构函数
AbsGoods(){} //无参构造函数

int _Price;
};

class Mp3:public AbsGoods
{
public:
Mp3()
{
cout<<"MP3 creator func"<<endl;
_mName = "MP3";
_Price = 200;
} //构造函数

Mp3(const Mp3& another)  //拷贝构造函数
{
cout<<"MP3 copy func"<<endl;
_mName = another._mName;
_Price = another._Price;
}

virtual AbsGoods* Clone() //实现统一的接口克隆函数
{
return new Mp3(*this); //根据函数指针以及随身的虚函数指针调用MP3的拷贝构造函数
}
};

class Computer:public AbsGoods
{
public:
Computer()
{
cout<<"Computer creator func"<<endl;
_mName = "COMPUTER";
_Price = 4000;
}

Computer(const Computer& another)
{
cout<<"Computer copy func"<<endl;
_mName = another._mName;
_Price = another._Price;
}

virtual AbsGoods* Clone() //实现统一的接口克隆函数
{
return new Computer(*this);
}
};

class Person{
public:
Person(){_CountGoods = 0;}

bool addGoods(AbsGoods* aGoods)
{
if(NULL == aGoods) return false;
if(_CountGoods >= 10)
return false;
_myGoods[_CountGoods] = aGoods;
_CountGoods++;
return true;
}

void out()
{
for(int i=0; i<_CountGoods; i++)
{
cout<<_myGoods[i]->_mName<<"  ";
}
}

AbsGoods* getGoods(int Num)
{
return _myGoods[Num-1];
}

int getGoodsNum()
{
return _CountGoods;
}

private:
AbsGoods* _myGoods[10];
int _CountGoods;
};

class Mical : public Person{
public:
static Mical* getMical()
{
if(NULL == _thisMical)
{
_thisMical = new Mical;
}
return _thisMical;
}
private:
Mical(){}
Mical(const Mical& another){}
void operator = (const Mical& another);
static Mical* _thisMical;  //单例模式,全局只有一个Mical
};

class Merry : public Person{
public:
static Merry* getMerry()
{
if(NULL == _thisMerry)
{
_thisMerry = new Merry;
}
return _thisMerry;
}
private:
Merry(){}
Merry(const Merry& another){}
void operator = (const Merry& another);
static Merry* _thisMerry;//单例模式,全局只有一个Merry
};

#endif


test.cpp

#include "Prototype.h"
#include <iostream>

using namespace std;

Mical* Mical::_thisMical = NULL;
Merry* Merry::_thisMerry = NULL;

int main()
{
Mical* mical = Mical::getMical();
Merry* merry = Merry::getMerry();

mical->addGoods(new Computer); //"Computer creator func"
mical->addGoods(new Mp3);  //"MP3 creator func"

for(int i=0; i<mical->getGoodsNum(); i++)
{
merry->addGoods(mical->getGoods(i+1)->Clone()); //"Computer copy func" "MP3 copy func"
}
cout<<"Mical's Goods : ";
mical->out(); //Mical's Goods: Computer MP3
cout<<endl;
cout<<"Merry's Goods : ";
merry->out(); //"Merry's Goods: Computer MP3"
return 0;
}


摘录知乎-陈硕的一句总结“Prototype 的意义在于,你拿到一个 Base* ,它指向某个 Derived 对象,你想克隆出 Derived 对象,但代码中不写出 Derived 的具体类型,因为有很多派生类,这种情况下你用构造函数是搞不定的,type-switch 是 bad smells 。”所以原型模式更应该叫做克隆模式,事实上,原型模式往往已经被集成在底层中,大家平时就在使用,如Java内核便已经使用原型模式配合工厂方法模式封装对象创建细节。

对象需求场景:

1. 说白了其实就是根据对象指针反推对象具体的子类型,并再次实例化该子类(同类的问题还有根据函数指针反推具体的函数返回类型、形参类型),从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节

2. 不想让client知道太多具体的子类信息,在client眼中只需要是一个统一的抽象父类即可;

3. 要实例化的对象的具体子类型要在runtime才能确定(即根据传送进来的对象指针才能知道要创建的那些具体子类);

使用思想:抽象与封装

评分: ★★

造轮子套路之五: 单例模式



#ifndef _SINGLETON_H
#define _SINGLETON_H

#include <iostream>
#include <mutex>

class Singleton
{
private:
Singleton(){}//私有化构造函数使得不能在外部构造实例,封死其他所有创建对象的入口
static Singleton * singleton;//静态变量
static mutex* mtx;

public:
/*线程不安全的传统单例模式*/
static Singleton * GetInstance()//提供接口创建对象
{
if (singleton == nullptr)//通过判断是否有实例存在,如果存在就返回该地址,这样就保证只有一个实例存在,但是实现并非线程安全
singleton = new Singleton;
return singleton;
}

/*大名鼎鼎的DCLP Doule Checked Locking Pattern*/
static Singleton* GetInstance()
{
if (singleton == nullptr)
{
mtx->lock();
if (singleton == nullptr)
{
singleton = new Singleton;
}
mtx->unlock();
}
return singleton;
}
};

Singleton * Singleton::singleton = nullptr;//静态类型初始化格式:<数据类型> <类名>::静态数据名=初始化值
Singleton mutex* mtx = mutex staticLock;

#endif


对象需求场景:单例模式是我个人非常喜欢的一个模式,虽然使用的场景特别有限,但是确实很精巧,往往有意想不到的效果

使用思想:封装

评分: ★★★
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息