zoukankan      html  css  js  c++  java
  • 设计模式之:创建型设计模式

    设计模式之:创建型设计模式(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;
    }
    复制代码
  • 相关阅读:
    龟兔赛跑(多线程练习题)
    进程和线程详解
    toString()方法详解
    使用IDEA的Git插件上传项目教程
    js运算符单竖杠“|”的用法和作用及js数据处理
    vue项目axios请求接口,后端代理请求接口404,问题出现在哪?
    jQuery的ajax的post请求json格式无法上传空数组
    es6 学习小记 扩展运算符 三个点(...)
    select2插件使用小记2
    js中多维数组转一维
  • 原文地址:https://www.cnblogs.com/MY0213/p/9939340.html
Copyright © 2011-2022 走看看