一、引言:

在前面我们学习了C#设计模式中的前十个模式——单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式、适配器模式、桥接模式、装饰者模式、组合模式。在继续学习后面的设计模式之前,把前面学习过的设计模式整理归纳复习一遍是很有必要的。下面让我们来复习一下这十个设计模式的一些基础概念。

二、各设计模式:

1.单例模式:

1.1、定义:同一时间内只允许一个实例进行操作,确保一个类只有一个实例,并提供一个访问它的全局访问点。

1.2、实现方法:

      1. 私有化构造函数,使外界不能创建该类实例。
      2. 声明一个静态变量接收类的实例。
      3. 定义一个静态共有方法提供全局访问。

 

2.简单工厂模式:

2.1、定义:由一个工厂对象决定创建出哪一种产品类的实例(难以扩展,一旦添加新产品就不得不修改简单工厂方法)。

2.2、优缺点:

2.2.1、优点

        1. 简单工厂模式解决了客户端直接依赖于具体对象的问题,客户端可以消除直接创建对象的责任,而仅仅是消费产品。简单工厂模式实现了对责任的分割。
        2. 简单工厂模式也起到了代码复用的作用,去购买笔记本的所有人只需要负责消费就可以了,并不需要在自己类中实现,此时简单工厂的方法就让所有客户共用了。

2.2.2、缺点

        1. 难以扩展,一旦添加新产品就不得不修改简单工厂方法。
        2. 工厂类一旦不能正常工作,整个系统都会受到影响。

 

3.工厂方法模式:

3.1、定义:把具体产品的创建推迟到子类中,工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样就可以允许系统不修改工厂类逻辑的情况下来添加新产品,克服了简单工厂模式中缺点。

3.2、成员角色:

      1. 抽象产品角色,具体产品的抽象类。任何具体产品都应该继承该类。
      2. 具体产品角色,实现抽象产品类对定义的抽象方法,由具体工厂类创建,它们之间有一一对应的关系。
      3. 具体工厂角色,用来创建具体产品。
      4. 抽象工厂角色,任何具体工厂都必须继承该抽象类。

 

4.抽象工厂模式:

4.1、定义:抽象工厂模式提供一个创建产品的接口来负责创建相关或依赖的对象,而不具体明确指定具体类,抽象工厂允许客户使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么。

4.2、使用前提:

      1. 一个系统不要求依赖产品类实例如何被创建、组合和表达的表达(所有工厂模式应用的前提)。
      2. 这个系统有多个系列产品,而系统中只消费其中某一系列产品。
      3. 系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端不需要依赖具体实现。

4.3、优缺点:

4.3.1、优点:将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展。

4.3.2、缺点:很难支持新种类产品的变化。这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了开发封闭原则。

 

5.建造者模式:

5.1、定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,使得建造代码与表示代码的分离,可以使客户端不必知道产品内部组成的细节,从而降低了客户端与具体产品之间的耦合度。建造者模式的本质是使组装过程和创建具体产品解耦。

5.2、实现要点:

      1. 在建造者模式中,指挥者是直接与客户端打交道的,指挥者将客户端创建产品的请求划分为对各个部件的建造请求,再将这些请求委派到具体建造者角色,具体建造者角色是完成具体产品的构建工作的,却不为客户所知道。
      2. 建造者模式主要用于“分步骤来构建一个复杂的对象”,其中“分步骤”是一个固定的组合过程,而复杂对象的各个部分是经常变化的。
      3. 产品不需要抽象类,由于建造模式的创建出来的最终产品可能差异很大,所以不大可能提炼出一个抽象产品类。
      4. 抽象工厂模式解决了“系列产品”的需求变化,而建造者模式解决的是 “产品部分” 的需要变化。
      5. 由于建造者隐藏了具体产品的组装过程,所以要改变一个产品的内部表示,只需要再实现一个具体的建造者就可以了,从而能很好地应对产品组成组件的需求变化。

 

6.原型模式:

6.1、定义:原型模式用一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的方法来创建出更多的同类型对象。

6.2、使用背景:当创建一个类的实例的过程很复杂,并且我们需要创建多个这样类的实例时,如果我们用new去创建这样的类实例,会增加创建类的复杂度和耗费更多的内存空间,因为这样在内存中分配了多个一样的类实例对象。因为每个类实例都是相同的,当我们需要多个相同的类实例时,没必要每次都使用new去创建相同的类实例对象,我们只需创建一个类实例对象,如果后面需要更多这样的实例,可以通过对原来对象拷贝一份来完成创建,这样在内存中不需要创建多个相同的类实例,从而减少内存的消耗和达到类实例的复用。

6.3、优缺点:

6.3.1、优点

        1. 原型模式向客户隐藏了创建新实例的复杂性。
        2. 原型模式允许动态增加或较少产品类。
        3. 原型模式简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要这样。
        4. 产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构。

6.3.2、缺点

        1. 每个类必须配备一个克隆方法。
        2.  配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

 

7.适配器模式:

7.1、定义:适配器模式将“将现存的对象”在新的环境中进行调用,使得新环境中不需要去重复实现已经存在了的实现而很好地把现有对象(指原来环境中的现有对象)加入到新环境来使用。适配器模式有类的适配器模式和对象的适配器模式两种形式。

7.2、实现思维:在适配器模式中,适配器可以是抽象类,并适配器模式的实现是非常灵活的,我们完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,适配器类可以根据参数参数可以返回一个合适的实例给客户端。把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能够在一起工作。

7.3、使用前提:

      1. 系统需要复用现有类,而该类的接口不符合系统的需求。
      2. 想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
      3. 对于对象适配器模式,在设计里需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。

 

8.桥接模式:

8.1、定义:桥接模式即将抽象部分与实现部分脱耦,实现抽象化与实现化的解耦,使它们相互独立互不影响到对方,使它们可以独立变化。

8.2、使用前提:

      1. 一个系统需要在构件的抽象化角色和具体化角色之间添加更多的灵活性,避免在两个层次之间建立静态的联系。
      2. 设计要求实现化角色的任何改变不应当影响客户端,或者实现化角色的改变对客户端是完全透明的。
      3. 需要跨越多个平台的图形和窗口系统上。
      4. 一个类存在两个独立变化的维度,且两个维度都需要进行扩展。

8.3、优缺点:

8.3.1、优点:

        1. 把抽象接口与其实现解耦。
        2. 抽象和实现可以独立扩展,不会影响到对方。
        3. 实现细节对客户透明,对用于隐藏了具体实现细节。

8.3.2、缺点: 

        1. 增加了系统的复杂度。

 

9.装饰者模式:

9.1、定义:以对客户透明的方式动态地给一个对象添加额外的职责,采用对象组合而非继承的方式实现了再运行时动态地扩展对象功能的能力,相比生成子类可以更灵活地增加功能,而且可以根据需要扩展多个功能,避免了单独使用继承带来的灵活性差和多子类衍生问题。

9.2、出现背景:在软件开发中,我们经常想要对一类对象添加不同的功能,如果此时利用继承来实现的话,就需要定义无数的类,这样就会导致 ”子类爆炸“问题,为了解决这个问题,装饰者模式出现了。

9.3、成员角色:

      1. 抽象构件角色:给出一个抽象接口,以规范准备接受附加责任的对象。
      2. 具体构件角色:定义一个将要接收附加责任的类。
      3. 装饰角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
      4. 具体装饰角色:负责给构件对象 ”贴上“附加的责任。

9.4、使用前提:

      1. 需要扩展一个类的功能或给一个类增加附加责任。
      2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
      3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能。

9.5、优缺点:

9.5.1、优点

        1. 装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活。
        2. 通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合。
        3. 装饰者模式有很好地可扩展性。

9.5.2、缺点

        1. 装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。

10.组合模式:

10.1、定义:组合模式简单对象和复合对象必须实现相同的接口,解耦了客户程序与复杂元素内部结构,从而使客户程序可以向处理简单元素一样来处理复杂元素。允许你将对象组合成树形结构来表现”部分-整体“的层次结构,使得客户以一致的方式处理单个对象以及对象的组合。

10.2、出现背景:在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,由于简单对象和复合对象在功能上区别,导致在操作过程中必须区分简单对象和复合对象,这样就会导致客户调用带来不必要的麻烦,然而作为客户,它们希望能够始终一致地对待简单对象和复合对象,然而组合模式就是解决这样的问题。

10.3、使用前提:

      1. 需要表示一个对象整体或部分的层次结构。
      2. 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

10.4、实现模式:

      1. 透明式的组合模式:抽象构建类中给参加组合的对象定义所有公共的接口及默认行为。
      2. 安全式的组合模式:把管理子对象的方法声明放在复合对象里面。

10.5、成员角色:

      1. 抽象构件(Component)角色:这是一个抽象角色,上在透明式的组合模式里,它给参加组合的对象定义出了公共的接口及默认行为,可以用来管理所有的子对象。在安全式的组合模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝结构对象给出。
      2. 树叶构件(Leaf)角色:树叶对象时没有下级子对象的对象,定义出参加组合的原始对象的行为。
      3. 树枝构件(Composite)角色:代表参加组合的有下级子对象的对象,树枝对象给出所有管理子对象的方法实现。

10.6、优缺点:

10.6.1、优点:

        1. 组合模式使得客户端代码可以一致地处理对象和对象容器,无需关系处理的单个对象,还是组合的对象容器。
        2. 将”客户代码与复杂的对象容器结构“解耦。
        3. 可以更容易地往组合对象中加入新的构件。

10.6.2、缺点:

        1. 使得设计更加复杂。客户端需要花更多时间理清类之间的层次关系。