设计模式之:创建型设计模式(6种)

 

创建型设计模式有: 共6种

  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)
  • 建造者模式(Builder)
  • 原型模式(Prototype)
  • 单例模式(Singleton)

 

简单工厂模式

功能:主要用于创建对象。新添加类时,不会影响以前的系统代码。核心思想是用一个工厂来根据输入的条件产生不同的类,然后根据不同类的virtual函数得到不同的结果。

优点: 适用于不同情况创建不同的类时

缺点: 客户端必须要知道基类和工厂类,耦合性差

模式结构:

  • Factory:工厂角色负责实现创建所有实例的内部逻辑
  • Product:抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
  • ConcreteProduct:具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

模式应用:

 

../_images/SimpleFactory.jpg

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中的工厂方法

 

../_images/FactoryMethod.jpg

举例:

日志记录器:

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

../_images/loger.jpg

 

C++举例:

复制代码
#include <string>
#include <iostream>
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模式)

功能:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

优点: 

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,此模式可以实现高内聚低耦合的设计目的。
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,很实用。
  • 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点: 

  • 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
  • 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。

模式应用:

  在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时,可以使用抽象工厂模式进行设计。

 

../_images/AbatractFactory.jpg

 

C++举例:

复制代码
#include <string>
#include <iostream>
#include <vector>
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;
}
复制代码

 

建造者模式(生成器模式)

功能:将一个复杂对象的构建表示分离,使得同样的构建过程可以创建不同的表示。

优点: 

  • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都独立,因此可以方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭”。

缺点: 

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,若产品之间的差异性很大,则不适合使用该模式,因此其使用范围受到一定限制。
  • 若产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

模式应用:

  很多游戏软件中,地图包括天空、地面、背景等组成部分,人物角色包括人体、服装、装备等组成部分,可以使用建造者模式对其进行设计,通过不同的具体建造者创建不同类型的地图或人物。

../_images/Builder.jpg

 

举例: KFC套餐

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

../_images/KFCBuilder.jpg

 

C++举例:

复制代码
#include <string>
#include <iostream>
#include <vector>
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#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

模式应用:

  一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键编号,否则会出现主键重复,因此该主键编号生成器必须具备唯一性,可以通过单例模式来实现。

../_images/Singleton.jpg

举例:

  在OS中,打印池(Print Spooler)是一个用于管理打印任务的应用程序,通过打印池用户可以删除、中止或者改变打印任务的优先级,在一个系统中只允许运行一个打印池对象,如果重复创建打印池则抛出异常。现使用单例模式来模拟实现打印池的设计。

 

C++举例:

懒汉式单例:

复制代码
#include <iostream>
#include <string>
#include <vector>
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;
    else
        cout<<"no"<<endl;
    return 0;
}
复制代码

 

原型模式

功能:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象创建另外一个可定制的对象,而且不需知道任何创建的细节。

优点:   

  一般在初始化的信息不发生变化的情况下,克隆是最好的办法,既隐藏了对象创建细节,又提高性能。其等于是不用重新初始化对象,而是动态地获得对象运行时的状态。

缺点: 

 

模式应用:

 

 

C++举例

复制代码
#include<iostream>
#include <vector>
#include <string>
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;
}
复制代码