设计模式之创建型模式

  GoF的设计模式一共23个,可以分为3大类:创建型、结构型和行为型,这篇文章主要讨论创建型。

  创建型的设计模式包括:简单工厂(Simple Factory)、工厂方法(Factory Method)、抽象工厂(Abstract Factory)、单例(Singleton)、构造者(Builder)和原型(Prototype),我们分别来讨论。

  我们首先来看工厂系列的3个设计模式,它们都主要是针对软件设计中的“开放-封闭”原则,即程序应该对扩展开放,对修改封闭。特别是当我们的程序采用XML+反射的方式来创建对象时,工厂模式的威力就完全展现出来了,这时我们可以通过维护配置文件的方式,来控制程序的逻辑。

  1)简单工厂,当我们的程序在实例化对象时,如果输入条件不一样,产生的对象也不一样,那么我们可以考虑使用简单工厂对不同的实例进行统一封装, UML结构如下:

    

  优点:封装了具体对象的实例化过程,Client端和具体对象解耦,同时ProductManager可以作成静态类或者Singleton对象,然后可以使用HashMap缓存具体对象(前提是对象没有时间依赖性),降低创建对象的次数。

  缺点:当增添一种新类型的对象时,需要修改Productmanager的代码(如果不采用XML)

  2)工厂方法,它是针对简单工厂的改进版,添加了对ProductManager的抽象,UML结构如下:

    

  优点:结构更加灵活,对于某种类型的对象来说,会有一个特定的对象工厂指向它,这样当我们需要添加一种新类型的产品时,只需要添加两个类,一个是具体产品类,一个是新产品的工厂类。这样更加灵活。

  缺点:结构开始变得复杂,而且最终还是需要Client端来确定究竟使用哪一个Factory(当然这个信息可以保存在上下文或者配置文件中)。

  3)抽象工厂,这个是最复杂的工厂模式,它用来生成一个产品线上的所有产品,我们假设一个产品线上包括多个产品,不同的产品线上的产品个数是一样的,这样我们需要一个针对产品线的抽象,并且很显然不同产品线上的产品是不可能混到一起的。对应的UML结构图如下:

  

  上图表明,一个产品线上的产品由IProduct1和IProduct2组成,客户端在获取产品时,这两个产品应该是同时返回的,因此对于IProductManager来说,它需要同时生成这两个对象。

  优点:对创建产品家族的行为高度抽象,添加一个产品线的逻辑比较清晰。

  缺点:当我们对产品线上的产品进行增加和删除时,对应的操作比较麻烦,所有的产品工厂都需要进行修改。

  4)单例,这是比较好理解的一个模式,从字面上说,就是程序在运行的过程中,希望在任意时刻,都只保留某个对象的唯一实例。对应的UML结构图如下:

  

  单例的实现方式一般包括几步:1)私有的指向自身的字段;2)私有构造函数;3)公开对私有字段进行实例化的方法。也有几种针对具体语言进行的改善,例如针对多线程采用double lock机制,采用常量方式定义私有字段、使用内嵌类来实例化字段等。

  我们也可以对单例进行一些适当的扩展,例如我们将对象的个数由1个变为N个,这就成了对象池。

  通常工厂模式中会使用到单例模式,特别是对于简单工厂来说。

  5)构造者,对于一些复杂对象来说,它可以分成多个不同的部分,在实例化时,不同部分之间实例化的顺序,有时会有严格的限制,这时我们就可以使用构造者模式了。对应的UML结构图如下:

  

  我们定义了IBuilder接口来实例化对应的不同部分,同时有一个方法来返回对象的实例。而Constructor类的Construct方法会按照业务逻辑依次调用实例化部分对象的方法,即BuildPartA、BuildPartB,这里的调用顺序,完全由业务逻辑来控制,最后可以调用GetProduct方法取得完整的对象实例。

  我们有时也会对上图进行修改,例如将GetProduct放到Constructor中,或者将Construct方法放入到GetProduct(取消Constructor)中。即使有这些变形,但是基本的思想是不变的。

  6)原型,我们在程序运行过程中,当需要有新的实例对象时,有时并不希望是从头创建一个对象,而是希望新的实例的状态和某个已存在的实例保持一致,这就是原型模式发挥作用的地方。对应的UML结构图如下:

  

  在.NET中,已经定义了IClonable接口来实现原型模式。需要注意在实现时,会有深拷贝和浅拷贝的区别,深拷贝会同时拷贝堆栈和堆上的内容,而浅拷贝只会拷贝堆栈上的内容。

  

posted @ 2013-04-14 23:20  李潘  阅读(5919)  评论(0编辑  收藏  举报