创建型设计模式(6种)学习
Linux服务器开发/后台架构师知识体系整理
创建型设计模式有: 共6种
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
- 建造者模式(Builder)
- 原型模式(Prototype)
- 单例模式(Singleton)
简单工厂模式
功能:主要用于创建对象。新添加类时,不会影响以前的系统代码。核心思想是用一个工厂来根据输入的条件产生不同的类,然后根据不同类的virtual函数得到不同的结果。
优点: 适用于不同情况创建不同的类时
缺点: 客户端必须要知道基类和工厂类,耦合性差
模式结构:
- Factory:工厂角色负责实现创建所有实例的内部逻辑
- Product:抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
- ConcreteProduct:具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

C++举例:
//基类
class COperation
{
public:int m_nFirst;int m_nSecond;virtual double GetResult(){double dResult=0;return dResult;}
};
//加法
class AddOperation : public COperation
{
public:virtual double GetResult(){return m_nFirst+m_nSecond;}
};
//减法
class SubOperation : public COperation
{
public:virtual double GetResult(){return m_nFirst-m_nSecond;}
};//工厂类
class CCalculatorFactory
{
public:static COperation* Create(char cOperator); //基类和工厂类是关联关系
};COperation* CCalculatorFactory::Create(char cOperator)
{COperation *oper;switch (cOperator){case '+':oper=new AddOperation();break;case '-':oper=new SubOperation();break;default:oper=new AddOperation();break;}return oper;
}//客户端
int main()
{int a,b;cin>>a>>b;COperation * op=CCalculatorFactory::Create('-'); //静态函数直接调用op->m_nFirst=a;op->m_nSecond=b;cout<<op->GetResult()<<endl;return 0;
}
工厂方法模式(多态工厂模式)
功能:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
优点:
- 向客户隐藏了哪种具体产品将被实例化的细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 系统中加新产品时,无须修改抽象工厂和抽象产品的接口,(但要修改客户端,以选择不同的具体工厂!),无须修改其他的具体工厂和产品,而只要添加一个具体工厂和具体产品即可。符合“开闭原则”。
缺点:
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,一定程度上增加了系统复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。.
模式应用:
JDBC中的工厂方法

举例:
日志记录器:
某系统日志记录器要求支持多种日志记录方式,如文件记录、数据库记录等,且用户可以根据要求动态选择日志记录方式, 现使用工厂方法模式设计该系统。

C++举例:
#include
#include
using namespace std;//实例基类,相当于Product(为了方便,没用抽象)
class LeiFeng
{
public:virtual void Sweep(){cout<<"雷锋扫地"<<endl;}
};//学雷锋的大学生,相当于ConcreteProduct(具体产品1)
class Student: public LeiFeng
{
public:virtual void Sweep(){cout<<"大学生扫地"<<endl;}
};//学雷锋的志愿者,相当于ConcreteProduct(具体产品2)
class Volenter: public LeiFeng
{
public :virtual void Sweep(){cout<<"志愿者"<<endl;}
};-----------------------------
//工场基类Creator
class LeiFengFactory
{
public:virtual LeiFeng* CreateLeiFeng() //返回值体现依赖关系{return new LeiFeng();}
};//工场具体类1(创建大学生产品的具体工厂)
class StudentFactory : public LeiFengFactory
{
public :virtual LeiFeng* CreateLeiFeng() //返回值体现依赖关系{return new Student();}
};//工场具体类2(创建志愿者产品的具体工厂)
class VolenterFactory : public LeiFengFactory
{
public:virtual LeiFeng* CreateLeiFeng(){return new Volenter(); //返回值体现依赖关系}
};-----------------------------
//客户端
int main()
{LeiFengFactory *sf=new StudentFactory(); //由客户端选创建什么产品LeiFeng *s=sf->CreateLeiFeng();s->Sweep();delete s;delete sf;return 0;
}
抽象工厂模式(Kit模式)
功能:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
优点:
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,此模式可以实现高内聚低耦合的设计目的。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,很实用。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
模式应用:
在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时,可以使用抽象工厂模式进行设计。

C++举例:
#include
#include
#include
using namespace std;//用户抽象接口(抽象产品A)
class IUser
{
public :virtual void GetUser()=0;virtual void InsertUser()=0;
};//部门抽象接口(抽象产品B)
class IDepartment
{
public:virtual void GetDepartment()=0;virtual void InsertDepartment()=0;
};//ACCESS用户(抽象产品A的产品1)
class CAccessUser : public IUser
{
public:virtual void GetUser(){cout<<"Access GetUser"<<endl;}virtual void InsertUser(){cout<<"Access InsertUser"<<endl;}
};//ACCESS部门(抽象产品B的产品1)
class CAccessDepartment : public IDepartment
{
public:virtual void GetDepartment(){cout<<"Access GetDepartment"<<endl;}virtual void InsertDepartment(){cout<<"Access InsertDepartment"<<endl;}
};//SQL用户(抽象产品A的产品2)
class CSqlUser : public IUser
{
public:virtual void GetUser(){cout<<"Sql User"<<endl;}virtual void InsertUser(){cout<<"Sql User"<<endl;}
};//SQL部门类(抽象产品B的产品2)
class CSqlDepartment: public IDepartment
{
public:virtual void GetDepartment(){cout<<"sql getDepartment"<<endl;}virtual void InsertDepartment(){cout<<"sql insertdepartment"<<endl;}
};//抽象工厂
class IFactory
{
public:virtual IUser* CreateUser()=0;virtual IDepartment* CreateDepartment()=0;
};//ACCESS工厂
class AccessFactory : public IFactory
{
public:virtual IUser* CreateUser(){return new CAccessUser();}virtual IDepartment* CreateDepartment(){return new CAccessDepartment();}
};//SQL工厂
class SqlFactory : public IFactory
{
public:virtual IUser* CreateUser(){return new CSqlUser();}virtual IDepartment* CreateDepartment(){return new CSqlDepartment();}
};//客户端:
int main()
{IFactory* factory= new SqlFactory();IUser* user=factory->CreateUser();IDepartment* depart = factory->CreateDepartment();user->GetUser();depart->GetDepartment();return 0;
}
建造者模式(生成器模式)
功能:将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
优点:
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都独立,因此可以方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭”。
缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,若产品之间的差异性很大,则不适合使用该模式,因此其使用范围受到一定限制。
- 若产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
模式应用:
很多游戏软件中,地图包括天空、地面、背景等组成部分,人物角色包括人体、服装、装备等组成部分,可以使用建造者模式对其进行设计,通过不同的具体建造者创建不同类型的地图或人物。

举例: KFC套餐
套餐是一个复杂对象,一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、 可乐等)等部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客。

C++举例:
#include
#include
#include
using namespace std;//最终的产品类
class Product
{
private:vector<string> m_product;
public:void Add(string strtemp){m_product.push_back(strtemp);}void Show(){vector<string>::iterator p=m_product.begin();while (p!=m_product.end()){cout<<*p<<endl;p++;}}
};//建造者基类
class Builder
{
public:virtual void BuilderA()=0;virtual void BuilderB()=0;virtual Product* GetResult()=0;
};//第一种建造方式
class ConcreteBuilder1 : public Builder
{
private:Product* m_product; //体现依赖关系
public:ConcreteBuilder1(){m_product=new Product();}virtual void BuilderA(){m_product->Add("one");}virtual void BuilderB(){m_product->Add("two");}virtual Product* GetResult(){return m_product;}
};//第二种建造方式
class ConcreteBuilder2 : public Builder
{
private:Product * m_product; //体现依赖关系
public:ConcreteBuilder2(){m_product=new Product();}virtual void BuilderA(){m_product->Add("A");}virtual void BuilderB(){m_product->Add("B");}virtual Product* GetResult(){return m_product;}
};//指挥者类
class Direct
{
public:void Construct(Builder* temp) //构建一个使用Builder接口的对象{temp->BuilderA();temp->BuilderB();}
};//客户端
int main()
{Direct *p=new Direct();Builder* b1=new ConcreteBuilder1();Builder* b2=new ConcreteBuilder2();p->Construct(b1); //调用第一种建造方式Product* pb1 = b1->GetResult();pb1->Show();p->Construct(b2); //调用第二种建造方式Product * pb2 = b2->GetResult();pb2->Show();return 0;
}
单例模式
功能:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
优点:
- 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。
缺点:
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。
模式应用:
一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键编号,否则会出现主键重复,因此该主键编号生成器必须具备唯一性,可以通过单例模式来实现。

举例:
在OS中,打印池(Print Spooler)是一个用于管理打印任务的应用程序,通过打印池用户可以删除、中止或者改变打印任务的优先级,在一个系统中只允许运行一个打印池对象,如果重复创建打印池则抛出异常。现使用单例模式来模拟实现打印池的设计。
C++举例:
懒汉式单例:
#include
#include
#include
using namespace std;class Singelton
{
private:Singelton(){ } //构造函数私有堵死了外界创建副本的可能!!!static Singelton* singel; //私有的静态全局变量来保存该类的唯一实例public:static Singelton* GetInstance() //获得本类实例的唯一全局访问点{if(singel == NULL){singel = new Singelton();}return singel;}};
Singelton* Singelton::singel = NULL;//注意静态变量类外初始化//客户端:
int main()
{Singelton* s1=Singelton::GetInstance();Singelton* s2=Singelton::GetInstance();if(s1 == s2) //比较两次实例化后的结果是实例相同!!!cout<<"ok"<<endl;elsecout<<"no"<<endl;return 0;
}
原型模式
功能:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象创建另外一个可定制的对象,而且不需知道任何创建的细节。
优点:
一般在初始化的信息不发生变化的情况下,克隆是最好的办法,既隐藏了对象创建细节,又提高性能。其等于是不用重新初始化对象,而是动态地获得对象运行时的状态。
缺点:
原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候。
模式应用:

C++举例
#include
#include
#include
using namespace std;//抽象基类
class Prototype
{
private:string m_strName;public: Prototype(string strName){ m_strName = strName; }Prototype() { m_strName = " "; }void Show() {cout<<m_strName<<endl;}virtual Prototype* Clone() = 0 ; //关键就在于这样一个抽象方法
} ; // class ConcretePrototype1
class ConcretePrototype1 : public Prototype
{
public: ConcretePrototype1(string strName) : Prototype(strName){}ConcretePrototype1(){}virtual Prototype* Clone() { ConcretePrototype1 *p = new ConcretePrototype1() ; *p = *this ; //复制对象 return p ; }
} ; // class ConcretePrototype2
class ConcretePrototype2 : public Prototype
{
public: ConcretePrototype2(string strName) : Prototype(strName){}ConcretePrototype2(){}virtual Prototype* Clone() { ConcretePrototype2 *p = new ConcretePrototype2() ; *p = *this ; //复制对象 return p ; }
} ; //客户端
int main()
{ConcretePrototype1* test = new ConcretePrototype1("小王");ConcretePrototype2* test2 = (ConcretePrototype2*)test->Clone();test->Show();test2->Show();return 0;
}
C/C++Linux服务器开发/后台架构师视频学习
面试题、学习资料、教学视频和学习路线图,免费分享有需要的可以自行添加学习交流群960994558
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
