C#设计模式之总结篇

一、引言

     C#版本的23种设计模式已经写完了,现在也到了一个该总结的时候了。说起设计模式,我的话就比较多了。刚开始写代码的时候,有需求就写代码来解决需求,如果有新的需求,或者需求变了,我就想当然的修改自己的代码来满足新的需求,这样做感觉是理所当然的,也没感觉有什么不妥的地方。写了两年多代码,偶尔一次,听说了设计模式,据听说设计模式就是软件界的“独孤九剑”,学会之后就可以天下无敌,于是我就开始了我的进修之路。

       想当初,我就像很多的初学编程的人一样,在面试官面前很容易说出面向对象语言的三大特征:继承,多态和封装,其实,我根本不理解这三个词语的意思,或者说理解的很浅薄。当然也有很多概念是不清楚的,比如:OOPL(面向对象语言)是不是就是OO的全部,面向对象的设计模式和设计模式的到底有什么区别,等等相关问题。自从自己学了设计模式,很多概念变得清晰明了,做事有原则了,或者说有准则了,不像以前只是解决了问题就好,根本不管代码写的是否合理。写的代码可以很优美了,结构更合理了,自己开始重新思考代码这个东西。

     学习设计模式并不是一帆风顺的,尤其是刚开始的时候,由于自己很多概念不是很清楚,也走了很多弯路,但是通过自己的坚持,通过自己不断的看,不断的写,对模式的理解也越来越准确了。写代码不是一个很简单的事情,做任何事都是有原则的,写代码也一样,如果自己心中对做的事情没有准则,那就和无头的苍蝇一样,做与不做是一样的。写代码和写好代码是不一样的,如果要写好的代码,考虑的问题更多了,考虑稳定性,扩展性和耦合性,当然也要考虑上游和下游关联的问题,让你做事的时候知道怎么做,也知道为什么这么做,这就是学习设计模式带来的好处。

     好了,说了也不少了,我把我写的模式都罗列出来,每个模式都有链接,可以直接点击进入查看和阅读,希望对大家阅读有益。

系列导航:

创建型:

    C#设计模式(1)——单例模式(Singleton Pattern)

    C#设计模式(2)——工厂方法模式(Factory Pattern)

    C#设计模式(3)——抽象工厂模式(Abstract Pattern)

    C#设计模式(4)——建造模式(Builder Pattern)

    C#设计模式(5)——原型模式(Prototype Pattern)

结构型:

    C#设计模式(6)——适配器模式(Adapter Pattern)

    C#设计模式(7)——桥接模式(Bridge Pattern)

    C#设计模式(8)——装饰模式(Decorator Pattern)

    C#设计模式(9)——组合模式(Composite Pattern)

    C#设计模式(10)——外观模式(Facade Pattern)

    C#设计模式(11)——享元模式(Flyweight Pattern)

    C#设计模式(12)——代理模式(Proxy Pattern)

行为型:

    C#设计模式(13)——模板方法模式(Template Method)

    C#设计模式(14)——命令模式(Command Pattern)

    C#设计模式(15)——迭代器模式(Iterator Pattern)

    C#设计模式(16)——观察者模式(Observer Pattern)

    C#设计模式(17)——中介者模式(Mediator Pattern)

    C#设计模式(18)——状态模式(State Pattern)

    C#设计模式(19)——策略模式(Stragety Pattern)

    C#设计模式(20)——责任链模式(Chain of Responsibility Pattern)

    C#设计模式(21)——访问者模式(Vistor Pattern)

    C#设计模式(22)——备忘录模式(Memento Pattern)

    C#设计模式(23)——解释器模式(Interpreter Pattern)


二、 面向对象的设计原则

       写代码也是有原则的,我们之所以使用设计模式,主要是为了适应变化,提高代码复用率,使软件更具有可维护性和可扩展性。如果我们能更好的理解这些设计原则,对我们理解面向对象的设计模式也是有帮助的,因为这些模式的产生是基于这些原则的。这些规则是:单一职责原则(SRP)、开放封闭原则(OCP)、里氏代替原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)、合成复用原则(CRP)和迪米特原则(LoD)。下面我们就分别介绍这几种设计原则。

   2.1、单一职责原则(SRP):

  (1)、SRP(Single Responsibilities Principle)的定义:就一个类而言,应该仅有一个引起它变化的原因。简而言之,就是功能要单一。

  (2)、如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。(敏捷软件开发)

  (3)、软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。

     小结:单一职责原则(SRP)可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。责任过多,引起它变化的原因就越多,这样就会导致职责依赖,大大损伤其内聚性和耦合度。

   2.2、开放关闭原则(OCP)

  (1)、OCP(Open-Close Principle)的定义:就是说软件实体(类,方法等等)应该可以扩展(扩展可以理解为增加),但是不能在原来的方法或者类上修改,也可以这样说,对增加代码开放,对修改代码关闭。

  (2)、OCP的两个特征: 对于扩展(增加)是开放的,因为它不影响原来的,这是新增加的。对于修改是封闭的,如果总是修改,逻辑会越来越复杂。

     小结:开放封闭原则(OCP)是面向对象设计的核心思想。遵循这个原则可以为我们面向对象的设计带来巨大的好处:可维护(维护成本小,做管理简单,影响最小)、可扩展(有新需求,增加就好)、可复用(不耦合,可以使用以前代码)、灵活性好(维护方便、简单)。开发人员应该仅对程序中出现频繁变化的那些部分做出抽象,但是不能过激,对应用程序中的每个部分都刻意地进行抽象同样也不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。

   2.3、里氏代替原则(LSP)
 
     (1)、LSP(Liskov Substitution Principle)的定义:子类型必须能够替换掉它们的父类型。更直白的说,LSP是实现面向接口编程的基础。

      小结:任何基类可以出现的地方,子类一定可以出现,所以我们可以实现面向接口编程。 LSP是继承复用的基石,只有当子类可以替换掉基类,软件的功能不受到影响时,基类才能真正被复用,而子类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

   2.4、依赖倒置原则(DIP)

     (1)、DIP(Dependence Inversion Principle)的定义:抽象不应该依赖细节,细节应该依赖于抽象。简单说就是,我们要针对接口编程,而不要针对实现编程。

     (2)、高层模块不应该依赖低层模块,两个都应该依赖抽象,因为抽象是稳定的。抽象不应该依赖具体(细节),具体(细节)应该依赖抽象。

      小结:依赖倒置原则其实可以说是面向对象设计的标志,如果在我们编码的时候考虑的是面向接口编程,而不是简单的功能实现,体现了抽象的稳定性,只有这样才符合面向对象的设计。

   2.5 接口隔离原则(ISP)

     (1)、接口隔离原则(Interface Segregation Principle, ISP)指的是使用多个专门的接口比使用单一的总接口要好。也就是说不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。过于臃肿的接口是对接口的一种污染。

     (2)、使用多个专门的接口比使用单一的总接口要好。

     (3)、一个类对另外一个类的依赖性应当是建立在最小的接口上的。

     (4)、一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。

     (5)、“不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。

       小结:接口隔离原则(ISP)告诉我们,在做接口设计的时候,要尽量设计的接口功能单一,功能单一,使它变化的因素就少,这样就更稳定,其实这体现了高内聚,低耦合的原则,这样做也避免接口的污染。

   2.6 组合复用原则(CRP)

     (1)、组合复用原则(Composite Reuse Principle, CRP)就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。

     (2)、要使用好组合复用原则,首先需要区分”Has—A”和“Is—A”的关系。 “Is—A”是指一个类是另一个类的“一种”,是属于的关系,而“Has—A”则不同,它表示某一个角色具有某一项责任。导致错误的使用继承而不是聚合的常见的原因是错误地把“Has—A”当成“Is—A”.例如:鸡是动物,这就是“Is-A”的表现,某人有一个手枪,People类型里面包含一个Gun类型,这就是“Has-A”的表现。

     小结:组合/聚合复用原则可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏替换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

   2.7 迪米特法则(Law of Demeter)

      (1)、迪米特法则(Law of Demeter,LoD)又叫最少知识原则(Least Knowledge Principle,LKP),指的是一个对象应当对其他对象有尽可能少的了解。也就是说,一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。

      (2)、关于迪米特法则其他的一些表述有:只与你直接的朋友们通信;不要跟“陌生人”说话。

      (3)、外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。

       小结:迪米特法则的初衷是降低类之间的耦合,实现类型之间的高内聚,低耦合,这样可以解耦。但是凡事都有度,过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

三、创建型模式

    创建型模式就是用来解决对象实例化和使用的客户端耦合的模式,可以让客户端和对象实例化都独立变化,做到相互不影响。创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。

    3.1、单例模式(Singleton Pattern):解决的是实例化对象的个数的问题,该模式是把对象的数量控制为一个,该模式可以扩展,可以把实例对象扩展为N个对象,N>=2。比如对象池的实现。

       动机(Motivate):在软件系统构建的过程中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?如果指望使用者不使用构造器来重复创建对象,这是不对的。这应该是类设计者的责任,而不是使用者的责任。

       意图(Intent):保证一个类仅有一个实例,并提供一个该实例的全局访问点。

       具体结构图如下所示
      

       示例代码:

 1 /// <summary>
 2     /// 单例模式的实现
 3     /// </summary>
 4     public sealed class Singleton
 5     {
 6         // 定义一个静态变量来保存类的实例
 7         private static volatile Singleton uniqueInstance;
 8  
 9         // 定义一个标识确保线程同步
10         private static readonly object locker = new object();
11  
12         // 定义私有构造函数,使外界不能创建该类实例
13         private Singleton()
14         {
15         }
16  
17         /// <summary>
18         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
19         /// </summary>
20         /// <returns></returns>
21         public static Singleton GetInstance()
22         {
23             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
24             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
25             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
26             // 双重锁定只需要一句判断就可以了
27             if (uniqueInstance == null)
28             {
29                 lock (locker)
30                 {
31                     // 如果类的实例不存在则创建,否则直接返回
32                     if (uniqueInstance == null)
33                     {
34                         uniqueInstance = new Singleton();
35                     }
36                 }
37             }
38             return uniqueInstance;
39         }
40     }


    3.2、工厂方法模式(Factory Method Pattern):一种工厂生产一种产品,工厂类和产品类是一一对应的,他们是平行的等级结构,强调的是“单个对象”的变化。

       动机(Motivate):在软件系统的构建过程中,经常面临着“某个对象”的创建工作:由于需求的变化,这个对象(的具体实现)经常面临着剧烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”的变化,从而保持系统中“其他依赖对象的对象”不随着需求改变而改变?

       意图(Intent):定义一个创建对象的工厂接口,由其子类决定要实例化的类,将实际创建工作推迟到子类中。

       具体结构图如下所示:

             



    3.3、抽象工厂模式(Abstract Factory Pattern):该模式关注的是多批多系列相互依赖的产品的变化,比如:SQLConnection,SQLCommand,SqlDataReader,SqlDataAdapter,就是一批相互依赖的对象,他们变化可以产生OledbConnection,OledbCommand,OledbDataReader,OledbDataAdapter

       动机(Motivate):在软件系统中,经常面临着"一系统相互依赖的对象"的创建工作:同时,由于需求的变化,往往存在更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种"封装机制"来避免客户程序和这种"多系列具体对象创建工作"的紧耦合?

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

       具体结构图如下所示

           

       代码实例

  1 /// <summary>
  2         /// 下面以不同系列房屋的建造为例子演示抽象工厂模式
  3         /// 因为每个人的喜好不一样,我喜欢欧式的,我弟弟就喜欢现代的
  4         /// 客户端调用
  5         /// </summary>
  6         class Client
  7         {
  8             static void Main(string[] args)
  9             {
 10                // 哥哥的欧式风格的房子
 11                AbstractFactory europeanFactory= new EuropeanFactory();
 12                europeanFactory.CreateRoof().Create();
 13                europeanFactory.CreateFloor().Create();
 14                europeanFactory.CreateWindow().Create();
 15                europeanFactory.CreateDoor().Create(); 
 16 
 17    
 18                //弟弟的现代风格的房子
 19                AbstractFactory modernizationFactory = new ModernizationFactory();
 20                modernizationFactory.CreateRoof().Create();
 21                modernizationFactory.CreateFloor().Create();
 22                modernizationFactory.CreateWindow().Create();
 23                modernizationFactory.CreateDoor().Create();
 24                Console.Read();
 25           }
 26     }
 27    
 28       /// <summary>
 29       /// 抽象工厂类,提供创建不同类型房子的接口
 30      /// </summary>
 31       public abstract class AbstractFactory
 32      {
 33          // 抽象工厂提供创建一系列产品的接口,这里作为例子,只给出了房顶、地板、窗户和房门创建接口
 34          public abstract Roof CreateRoof();
 35          public abstract Floor CreateFloor();
 36          public abstract Window CreateWindow();
 37          public abstract Door CreateDoor();
 38      }
 39   
 40      /// <summary>
 41     /// 欧式风格房子的工厂,负责创建欧式风格的房子
 42     /// </summary>
 43     public class EuropeanFactory : AbstractFactory
 44     {
 45          // 制作欧式房顶
 46          public override Roof CreateRoof()
 47          {
 48               return new EuropeanRoof();
 49           }
 50   
 51          // 制作欧式地板
 52          public override Floor CreateFloor()
 53          {
 54              return new EuropeanFloor();
 55          }
 56  
 57         // 制作欧式窗户
 58         public override Window CreateWindow()
 59         {
 60              return new EuropeanWindow();
 61         }
 62  
 63         // 制作欧式房门
 64         public override Door CreateDoor()
 65         {
 66              return new EuropeanDoor();
 67          }
 68     }
 69   
 70      /// <summary>
 71     /// 现在风格房子的工厂,负责创建现代风格的房子
 72     /// </summary>
 73     public class ModernizationFactory : AbstractFactory
 74     {
 75          // 制作现代房顶
 76         public override Roof CreateRoof()
 77         {
 78              return new ModernizationRoof();
 79          }
 80 
 81         // 制作现代地板
 82        public override Floor CreateFloor()
 83        {
 84             return new ModernizationFloor();
 85        }
 86  
 87        // 制作现代窗户
 88       public override Window CreateWindow()
 89       {
 90            return new ModernizationWindow();
 91       }
 92  
 93       // 制作现代房门
 94      public override Door CreateDoor()
 95      {
 96            return new ModernizationDoor();
 97       }
 98   }
 99    
100     /// <summary>
101     /// 房顶抽象类,子类的房顶必须继承该类
102    /// </summary>
103    public abstract class Roof
104     {
105          /// <summary>
106         /// 创建房顶
107        /// </summary>
108        public abstract void Create();
109    }
110   
111    /// <summary>
112   /// 地板抽象类,子类的地板必须继承该类
113   /// </summary>
114   public abstract class Floor
115    {
116         /// <summary>
117        /// 创建地板
118       /// </summary>
119      public abstract void Create();
120   }
121   
122    /// <summary>
123   /// 窗户抽象类,子类的窗户必须继承该类
124   /// </summary>
125    public abstract class Window
126     {
127         /// <summary>
128        /// 创建窗户
129       /// </summary>
130       public abstract void Create();
131    }
132  
133    /// <summary>
134    /// 房门抽象类,子类的房门必须继承该类
135    
  • 6
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值