设计模式的一点总结和思考(一)创建型

面向接口编程

对于当前不知道或无法确定的东西,我们就抽象它,只对其接口操作,即现在不知道具体的涉及对象,但我知道如何使用它,先用其接口,待以后知道了具体的对象之后,再绑定上即可,这就是所谓的封装变化。

虽然不确定目标是谁,但可以确定如何使用目标。

多种多样的设计模式其实做的就是 封装变化 ,面对不同的情景,分析什么是变化的,什么是不变的,封装变化,使上层代码能够“以不变应万变”。

简单工厂

【严格来说其并不算是一种设计模式,其就是把一堆判断创建的语句放在了一个函数中,通过传入的参数决定创建哪一个产品的实例】

在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。(这也是其优点:把实现对象的创建和对象的使用分离)

在实际开发中,还可以在调用时将所传入的参数保存在XML等格式的配置文件中,修改参数时无须修改任何源代码。

工厂类将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。

class Factory
{
public:
    static Product* CreateProduct(int n )
    {
        switch(n)
        {
        case 1:
            return new FirstProduct;
            break;
        case 2:
            return new SecondProduct;
            break;
        }
    }
};

【适用环境】

  • 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  • 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。

由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。


工厂方法

【产品主体延迟到子类决定,产品的操作由基类实现,子类只负责具体创建某产品】

(变化的是产品,不变的是对产品的操作)

【动机】

由于简单工厂模式的局限性,比如:工厂现在能生产ProductA、ProductB和ProductC三种产品了,此时,需要增加生产ProductD产品;那么,首先是不是需要在产品枚举类型中添加新的产品类型标识,然后,修改Factory类中的switch结构代码。这种对代码的修改量较大,易产生编码上的错误。

工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。

相似的产品有相同的操作,我们对产品的接口操作,让产品的生成延迟到子类。(这些操作是固定的,但操作的目标是变化的)

不同的子类生产不同的产品,你选择了哪种子类,就相当于你选择了哪种产品。

简单示例代码:

class Factory
{
public:
    virtual Product *CreateProduct() = 0;
    void OperateProduct() ;  //对产品的操作框架
private:
    Product * productPtr ;
};

class FactoryA : public Factory
{
public:
    Product *CreateProduct()
    {
        return new ProductA ();
    }
};

class FactoryB : public Factory
{
public:
    Product *CreateProduct()
    {
        return new ProductB ();
    }
};

//对产品的操作,这些产品有共同的接口
void Factory::OperateProduct()
{
    //我们不知道具体是哪种产品被创建
    Product * productPtr = CreateProduct() ;
    //但是我们知道怎么使用这个产品
    productPtr->show();
}
int main(void)
{
    //当我们选择了不同的工厂,我们实际就选择了相应的产品
    Factory *factory = new FactoryB ();
    factory->OperateProduct();

    //........
}

【思考】

1、这和我们直接new一个产品对象有什么区别?

    Product* product = new ProductB() ;
    product->show() ;
    product->change() ;
    区别是我们的工厂类中有围绕着产品进行的对产品的操作。我们使用这个操作框架(我们保证这个操作框架是基本不变的)。我们直接对产品进行操作而不通过工厂类,则相同的对产品的操作代码会散落在各处,代码重用性不好。
    我们就是在一个框架中使用工厂方法的,框架使用抽象类定义对象之间的关系,这些对象的创建通常也由框架负责。(我们就是为了代码重用才使用框架的,不会说是直接使用产品类,在客户代码中再重写这些与产品相关的框架代码)

    【类似模板方法】
    工厂方法通常在模板方法中被调用。

2、使用模板以避免创建子类

    从上面的例子看到,在使用工厂方法时,我们要new一个相应的工厂类,然后在使用完毕后需要delete它。这样做比较繁琐。
    解决方式:我们可以把new的工厂类放入auto_ptr智能指针类,由智能指针负责管理我们动态分配的对象。
    或者,把工厂类设计成模板类

即:

template <class Product>
Product* StdFactory : public Factory
{
public:
    virtual Product* CreateProduct()
    {
        return new Product ;
    }
}

使用这个模板,我们就不需要再new工厂的子类了
StdFactory<ProductB> FactoryB ;

【缺点】

  • 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。

【使用场景】

  • 1、当一个类不知道它所必须创建的对象的类的时候。(我现在需要使用这个类对象,但我又不知道这个类对象如何实例化)
  • 2、当一个类希望由它的子类来指定它所创建的对象的时候。

相关知识:

C++的智能指针(我们可以在工厂类中添加一个智能指针来存储new的产品对象)


抽象工厂

【把一系列相关的组件,放在一个工厂创建,这样只需换一个工厂,就可以换一个产品系列】

(变化的是产品的形态种类,不变的是对产品的操作集)

抽象工厂是诸多设计模式中最为“宏伟”的一个模式。(这里说的宏伟,意为更换一个抽象工厂,对系统的面貌影响较大)

【动机】

  • 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。

可以用“一横一竖”来概括抽象工厂。

  • 一横(产品的继承结构):横向扩展,平级的产品,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机。抽象电视机是父类,而具体品牌的电视机是其子类。
  • 一竖(产品族):指由同一个工厂生产的,位于不同产品结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品结构中,海尔电冰箱位于电冰箱产品结构中。

当你要强调一系列相关的产品对象的设计以便进行联合使用时。

【缺点】

  • 纵向扩展比较困难比如添加一个新的产品种类(比如:手机产品),这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。(需要对海尔厂、海信厂、TCL厂都进行修改,添加它们的手机产品)

【使用场景】

  • 一个系统要由多个产品系列中的一个来配置时。
  • 当你要强调一系列相关产品对象的设计以便进行联合使用时。

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

一个抽象工厂创建了一个完整的产品系列。

抽象工厂只负责创建这些种类的产品,而不负责组装这些产品为成品。


建造者模式

【将一个对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示】

(变化的是零件,不变的是对这些零件的组装过程)

【动机】

  • 无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

    复杂对象相当于一辆有待建造的汽车,而对象的属性相当于汽车的部件,建造产品的过程就相当于组合部件的过程。由于组合部件的过程很复杂,因此,这些部件的组合过程往往被“外部化”到一个称作建造者的对象里,建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式,这就是建造者模式的模式动机。

这样的话,我们可以方便地改变产品的内部表示,即可以方便地更换零件,产品的总结构不变。

示例代码

int main(void)
{
    ConcreteBuilder * builder = new ConcreteBuilder();
    Director  director;
    director.setBuilder(builder);
    Product * pd =  director.constuct();
    pd->show();

    //......
}

Product* Director::constuct()
{
    m_pbuilder->buildPartA();
    m_pbuilder->buildPartB();
    m_pbuilder->buildPartC();

    return m_pbuilder->getResult();
}

【优点】

  • 在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。(有点像抽象工厂)

【缺点】

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式。

【适用情景】

  • 需要生成的产品对象有复杂的内部结构,且这些产品对象的属性相互依赖,需要指定其生成顺序。
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

【就是,当需要创建由许多零件装配的对象时,使用建造者模式】

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

【总结】

    Builder类与抽象工厂类十分相似,但抽象工厂创建的是一系列有关的产品,建造者模式创建的是一系列有关的零件,最后还需要导演类来组装零件成产品。(多了一个导演类来组装零件,两者的关注点不同)
    注意:导演只是对组装过程进行引导,最终还是builder组装并产出产品。(导演类中是包含建造者的,所有创建工作由建造者负责完成)导演调用builder一步一步把零件填装到其空骨架中,最终返回一个成品。
    如果将抽象工厂模式看成 汽车配件生产工厂 ,生产一个产品族的产品,那么建造者模式就是一个 汽车组装工厂 ,通过对部件的组装可以返回一辆完整的汽车。

    将工厂模式稍加变化可以得到建造者(Builder)模式。工场模式的“加工工艺”是隐藏的,而建造模式的“加工工艺”是暴露的。把工厂方法的一个方法分成做个方法步骤去构造一个产品,即为建造者模式。

单件模式

【用static函数返回此单件的指针】

懒汉式

懒汉式的特点是延迟加载,比如配置文件的单例,采用懒汉式的方法,顾名思义,懒汉么,很懒的,配置文件的实例直到用到的时候才会加载。

class Singleton
{
public:
    static Singleton* Instance() ;
protected:
    Singleton() ;
private:
    static Singleton* _instance ;
} ;

//实现
Singleton* Singleton::_instance = 0 ;
Singleton* Singleton::Instance()
{
    if (_instance == 0)
    {
        _instance = new Singleton ;
    }

    return _instance ;
}

这种简单的懒汉式单例,new出来的东西始终没有释放,虽然只有一份,不太可能造成内存泄露。

但我们可以在单例类中添加一个静态对象专门用来释放new出来的单例。

改进版懒汉:

class Singleton
{
public:
    static Singleton * GetInstance()
    {
        if(_instance == NULL)
            _instance = new CSingleton();
        return _instance;
    }
protected:
    CSingleton() { }
private:
    static Singleton *_instance; 

    class CFreeInstance
    {
    public:
        ~CFreeInstance()
        {
            if(Singleton::_instance)
                delete CSingleton::_instance;
        }
    };
    static CFreeInstance aFree;   

};

饿汉式:

懒汉式单例有线程安全问题,在if判断_instance是否为NULL时,多线程访问会出现安全问题。

饿汉式的特点是一开始就加载了,饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变。

class Singleton
{
private:
    Singleton() { }
public:
    static Singleton * GetInstance()
    {
        static Singleton _instance;
        return &_instance;
    }
}; 


【参考】

《设计模式之禅》

《GoF设计模式》

时间: 2024-10-05 13:53:24

设计模式的一点总结和思考(一)创建型的相关文章

Java开发中的23种设计模式详解之一:5种创建型模式

一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 其实还有两类:并发型模式和线程池模式.用一个图片来整体描述一下: 二.设计模式的六大原则 1.开闭原则(Open Clo

跟着实例学习设计模式(3)-工厂方法(创建型)

工厂方法属于创建型设计模式. 设计意图:定义一个用于创建对象的接口.让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类. 静态工厂使用面向对象的方式,有效的攻克了添加新产品给client和实现类带来的代码改动问题.封装了创建过程,减低了添加新产品带来的代码改动错误.可是新增功能须要改动client代码和工厂创建类的推断逻辑,这种设计违背了开放-封闭原则.对扩展开放.对改动封闭,那我们就须要找一种方式避免添加新的功能时改动工厂创建方法的逻辑.(毕竟会对原有代码做改动难免会有失误) 工厂

跟着实例学习设计模式(4)-抽象工厂(创建型)

抽象工厂属于创建型设计模式 设计意图:提供一个接口.能够创建一系列相关或相互依赖的对象,而无须指定它们详细的类. 光看设计意图有些抽象,不好理解.让我们来看一下实例类图,结合类图我们再做详细的解释,相信会让大家豁然开朗的.我们以生产汽车为例,我们生产的汽车分两个系列,小车.卡车,每一个系列汽车都有发动机和油箱. 上图: IAbstrcatFactory:抽象工厂接口,声明创建抽象产品的方法. CarFactory:小车工厂实现类. TrunkFactory:卡车工厂实现类. 这里我们为每一个系列

设计模式05: Prototype 原型模式(创建型模式)

Prototype 原型模式(创建型模式) 依赖关系的倒置抽象不应该依赖于实现细节,细节应该依赖于抽象.对所有的设计模式都是这样的. -抽象A直接依赖于实现细节b -抽象A依赖于抽象B,实现细节b依赖于抽象B 动机(Motivation) 在软件工程中,经常面临着“某些结构复杂的对象”的创建工作:由于需求变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口. 如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出这些“易变对象”,从而使得“依赖这些易变对象的客户程序”不

设计模式03: Builder 生成器模式(创建型模式)

Builder生成器模式(创建型模式) Builder模式缘起假设创建游戏中的一个房屋House设施,该房屋的构建由几个部分组成,且各个部分富于变化.如果使用最直观的设计方法,每个房屋部分的变化,都将导致房屋构建的重新修正... 动机(Motivation)在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成:由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将他们组合在一起的算法却非常稳定.如何应对这种变化?如何提供一种“封装机制”来隔离

设计模式(二)单例模式Singleton(创建型)

几乎所有面向对象的程序中,总有一些类的对象需要是唯一的,例如,通过数据库句柄到数据库的连接是独占的.您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销.再如大家最经常用的IM,如QQ,在同一台电脑,一个帐号只能有唯一的登录. 1. 问题 怎样确保一个特殊类的实例是独一无二的(它是这个类的唯一实例),并且这个实例易于被访问呢? 2. 解决方案 1)全局变量:一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象.因为你的任何代码都能修改全局变量,这将不可避免的引起

设计模式(三)建造者模式(创建型)

定义:将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 使用场景: 创建的产品有复杂的内部结构,且创建的产品有较多的共同点,其组成部分相似.隔离复杂对象的创建,使得相同的过程可以创建不同的产品. 与抽象工厂的区别: 在建造者模式里,有个指导者,由指导者来管理创造者,用户是与指导者联系的,指导者联系创建者得到最后的产品. 角色: Builder:一个抽象接口,用于规范定义复杂对象的组成部分. ConcreteBuilder:实现Builder接口,具体化复杂对象的各个部

设计模式(一)工厂模式(创建型)

简单工厂模式 一个抽象产品类,可以派生出多个具体产品类: 一个具体工厂类,可以创建多个具体产品的实例. 抽象产品类定义 public abstract class Sender { void send(){ }; } 具体产品类定义 邮件产品类 public class MailSender extends Sender{ public void send(){ System.out.println("Mail Sender!"); } } 短信产品类 public class Sms

设计模式C++代码实例(一) - 创建型模式

 1.简单工厂模式(Simple Factory Pattern) 一个工厂生产所有的产品,根据输入参数决定产品的种类. #include "Factory.h" #include "ProductA.h" #include "ProductB.h" Product* Factory::createProduct(string proname) { if ("A" == proname) { return new Produc