【软件构造】(转) 设计模式

设计模式

    在课程中,咱们陆续学习了所有的24种设计模式,在实验中也用到了工厂模式、抽象工厂模式、策略模式等内容;在课程的考试中,咱们也将使用制定的设计模式对代码进行优化,为了更好的理解设计模式,转发本篇斯认为质量较好的博文,但愿能对设计模式有更明晰的认识。html

  转自: 设计模式大杂烩(24种设计模式的总结以及学习设计模式的几点建议)  做者:zuoxiaolong8810(左潇龙),转载请注明出处。android

         迄今为止,LZ已经将24种设计模式介绍完了,其中包括GOF23种设计模式以及简单工厂模式,这些设计模式之间并非彻底独立的,而是互相之间,会有一些相同的影子,下面咱们来一块儿总结下这24种设计模式。算法

 

模式分类 & 传送门 & 对比维度说明

 

        以上即是设计模式的分类以及各个模式的传送门,能够看到其中行为型模式的个数为最多,结构型次之,建立型设计模式最少。编程

        在写这篇文章的时候,LZ考虑的最多的一个问题就是,从哪几个维度去对比设计模式能让你们更加清楚的看出各个设计模式的区别与联系,思来想去,LZ决定从如下几个维度去对比设计模式。设计模式

 

  • 设计原则:描述每一个设计模式都遵循了哪些设计原则,破坏了哪些设计原则。
  • 经常使用场景:描述各个设计模式大部分状况下,都会在哪些场景下出现。
  • 使用几率:主要指在广泛的工做当中,该设计模式出现的频率,如果类库或是开源框架提供的功能中包含该模式,则也会计算其频率。
  • 复杂度:特指一个设计模式在实现的时候的复杂度,主要的衡量标准是类的数量、类之间的耦合关系。
  • 变化点:设计模式很大的一个意义在于容纳变化,掌握一个设计模式的变化点是很是重要的一件事。
  • 选择关键点:当选择使用一个设计模式的时候,指出最关键的选择点在哪里。
  • 逆鳞:龙有逆鳞,不可触摸,一样的,设计模式也有逆鳞,有些地方是不能碰的。
  • 相关设计模式:与其它设计模式的关系。

 

建立型设计模式

单例模式(Singleton Pattern 8-3)

  • 设计原则:无
  • 经常使用场景:应用中有对象须要是全局的且惟一
  • 使用几率:99.99999%
  • 复杂度:低
  • 变化点:无
  • 选择关键点:一个对象在应用中出现多个实例是否会引发逻辑上或者是程序上的错误
  • 逆鳞:在觉得是单例的状况下,却产生了多个实例
  • 相关设计模式
    • 原型模式:单例模式是只有一个实例,原型模式每拷贝一次都会创造一个新的实例。

简单工厂模式

  • 设计原则:遵循单一职责、违背开闭原则
  • 经常使用场景:须要在一堆产品中选择其中一个产品
  • 使用几率:99.99999%
  • 复杂度:低
  • 变化点:产品的种类
  • 选择关键点:一种产品是否可根据某个参数决定它的种类
  • 逆鳞:工厂类不能正常工做
  • 相关设计模式
    • 工厂方法模式:工厂方法模式是简单工厂模式的进一步抽象化,在这二者之间作选择,主要看将工厂进一步抽象化是否有必要,一般状况下,若是工厂的做用仅仅是用来制造产品,则不必使用工厂方法模式。

工厂方法模式(Factory Pattern 6-2)

  • 设计原则:遵循单一职责、依赖倒置、开闭原则
  • 经常使用场景:一种场景是但愿工厂与产品的种类对客户端保持透明,给客户端提供一致的操做,另一种是不一样的工厂和产品能够提供客户端不一样的服务或功能
  • 使用几率:60%
  • 复杂度:中低
  • 变化点:工厂与产品的种类
  • 选择关键点:工厂类和产品类是不是同生同灭的关系
  • 逆鳞:无
  • 相关设计模式
    • 抽象工厂模式:工厂方法模式与抽象工厂模式最大的区别在于,在工厂方法模式中,工厂创造的是一个产品,而在抽象工厂模式中,工厂创造的是一个产品族。

抽象工厂模式(Abstract Factory Pattern 6-2)

  • 设计原则:遵循单一职责、依赖倒置、开闭原则
  • 经常使用场景:须要一个接口能够提供一个产品族,且没必要知道产品的具体种类
  • 使用几率:30%
  • 复杂度:中
  • 变化点:工厂与产品的种类
  • 选择关键点:产品族是否须要一块儿提供,且是否有一致的接口
  • 逆鳞:无
  • 相关设计模式
    • 建造者模式:二者都是建造一批对象或者说产品,不一样的是二者的目的和实现手段,在建造者模式中,是为了复用对象的构建过程而定义了一个指挥者,而在抽象工厂模式中,是为了提供一个这批对象的建立接口而定义了抽象工厂接口。

建造者模式(Builder Pattern 6-2)

  • 设计原则:遵循单一职责、开闭原则
  • 经常使用场景:须要构建一批构建过程相同但表示不一样的产品,而构建过程很是复杂
  • 使用几率:10%
  • 复杂度:中
  • 变化点:产品的表示
  • 选择关键点:各个产品的构建过程是否相同
  • 逆鳞:指挥者不能正常工做

原型模式(Prototype Pattern 8-3)

  • 设计原则:无
  • 经常使用场景:须要在运行时动态的建立指定实例种类的对象,或是须要复用其状态
  • 使用几率:10%
  • 复杂度:中低
  • 变化点:无
  • 选择关键点:建立出来的对象是否能够当即投入使用
  • 逆鳞:在觉得是深度拷贝的状况下,却未实现深度拷贝

 

 

结构型设计模式

代理模式(Proxy Pattern 6-2)

  • 设计原则:体现功能复用
  • 经常使用场景:须要修改或屏蔽某一个或若干个类的部分功能,复用另一部分功能,可以使用静态代理,如果须要拦截一批类中的某些方法,在方法的先后插入一些一致的操做,假设这些类有一致的接口,可以使用JDK的动态代理,不然可以使用cglib
  • 使用几率:99.99999%
  • 复杂度:中高
  • 变化点:静态代理没有变化点,动态代理的变化点为具备相同切入点的类
  • 选择关键点:静态代理选择的关键点是是否要复用被代理的部分功能,动态代理选择的关键点在于可否在将被代理的这一批类当中,找出相同的切入点
  • 逆鳞:切入点的不稳定
  • 相关设计模式
    • 适配器模式:对于适配器模式当中的定制适配器,它与静态代理有着类似的部分,两者都有复用功能的做用,不一样的是,静态代理会修改一部分原有的功能,而适配器每每是所有复用,并且在复用的同时,适配器还会将复用的类适配一个接口

适配器模式(Adapter 5-3)

  • 设计原则:遵循开闭原则、体现功能复用
  • 经常使用场景:须要使用一个类的功能,可是该类的接口不符合使用场合要求的接口,可以使用定制适配器,又或者是有一个接口定义的行为过多,则能够定义一个缺省适配器,让子类选择性的覆盖适配器的方法
  • 使用几率:40%
  • 复杂度:中
  • 变化点:无
  • 选择关键点:定制适配器的选择关键点在因而否有更加优良的替代方案,缺省适配器的选择关键点在于接口中的方法是否能够不所有提供,且都有缺省方案
  • 逆鳞:无
  • 相关设计模式
    • 装饰器模式:对于适配器模式中的定制适配器与装饰器模式,两者都是使用组合加继承的手段,不一样的是,适配器模式的目的在于适配接口,装饰器模式的目的在于动态的添加功能,且能够叠加。

装饰器模式(Decorator 5-3)

  • 设计原则:遵循迪米特、单一职责、开闭原则,破坏里氏替换,体现功能复用
  • 经常使用场景:一个类须要动态的添加功能,且这些功能能够相互叠加
  • 使用几率:99.99999%
  • 复杂度:中
  • 变化点:动态添加的功能或者说装饰器
  • 选择关键点:添加的功能是否须要动态组装
  • 逆鳞:无

桥接模式(Bridge Pattern 6-2)

  • 设计原则:遵循单一职责、迪米特、开闭原则,体现功能复用
  • 经常使用场景:一个对象有多个维度的变化,须要将这些维度抽离出来,让其独立变化
  • 使用几率:20%
  • 复杂度:中高
  • 变化点:维度的扩展与增长
  • 选择关键点:是否能够将对象拆分红多个不相关的维度
  • 逆鳞:无

组合模式(Composite Pattern 6-2)

  • 设计原则:遵循依赖倒置、开闭原则,破坏接口隔离
  • 经常使用场景:当有一个结构能够组合成树形结构,且须要向客户端提供一致的操做接口,使得客户端操做忽略简单元素与复杂元素
  • 使用几率:30%
  • 复杂度:中
  • 变化点:节点的数量
  • 选择关键点:对外提供一致操做接口的结构是否可转化为树形结构
  • 逆鳞:结构不稳定或结构中的节点有递归关系

享元模式(Flyweight Pattern 8-3)

  • 设计原则:无
  • 经常使用场景:一些状态相同的对象被大量的重复使用
  • 使用几率:90%
  • 复杂度:中
  • 变化点:无
  • 选择关键点:被共享的对象是否能够将外部状态提取出来
  • 逆鳞:没有将外部状态提取彻底

外观模式(Facade 5-3)

  • 设计原则:遵循迪米特
  • 经常使用场景:一个子系统须要对外提供服务
  • 使用几率:60%
  • 复杂度:中
  • 变化点:无
  • 选择关键点:子系统对外提供服务是否须要依赖不少的类
  • 逆鳞:子系统对外提供的服务的变化或子系统自己的不稳定
  • 相关设计模式
    • 中介者模式:两者都是为了处理复杂的耦合关系,不一样的是外观模式处理的是类之间复杂的依赖关系,中介者模式处理的是对象之间复杂的交互关系

 

行为型设计模式

观察者模式(Observer Pattern 6-2)

  • 设计原则:遵循迪米特、开闭原则
  • 经常使用场景:须要将观察者与被观察者解耦或者是观察者的种类不肯定
  • 使用几率:40%
  • 复杂度:中
  • 变化点:观察者的种类与个数
  • 选择关键点:观察者与被观察者是不是多对一的关系
  • 逆鳞:观察者之间有过多的细节依赖

模板方法模式(Template method 5-3)

  • 设计原则:破坏里氏替换,体现功能复用
  • 经常使用场景:一批子类的功能有可提取的公共算法骨架
  • 使用几率:80%
  • 复杂度:中低
  • 变化点:算法骨架内各个步骤的具体实现
  • 选择关键点:算法骨架是否牢固
  • 逆鳞:无

命令模式(Command Pattern 6-2)

  • 设计原则:遵循迪米特、开闭原则
  • 经常使用场景:行为的请求者与行为的处理者耦合度太高
  • 使用几率:20%
  • 复杂度:中高
  • 变化点:命令的种类
  • 选择关键点:请求者是否不须要关心命令的执行只知道接受者
  • 逆鳞:命令的种类无限制增加
  • 相关设计模式
    • 职责链模式:容易将两者关联在一块儿的缘由是,两者都是为了处理请求或者命令而存在的,并且两者都是为了将请求者与响应者解耦,不一样的是命令模式中,客户端须要知道一个命令的接受者,在建立命令的时候就把接受者与命令绑定在一块儿发送给调用者,而职责链模式中,客户端并不关心最终处理请求的对象是谁,客户端只是封装一个请求对象,随后交给职责链的头部而已,也正由于这样,两者的实现方式,有着很大的区别

状态模式(State Pattern 6-3)

  • 设计原则:遵循单一职责、依赖倒置、开闭原则
  • 经常使用场景:一个对象在多个状态下行为不一样,且这些状态可互相转换
  • 使用几率:20%
  • 复杂度:中
  • 变化点:状态的种类
  • 选择关键点:这些状态是否常常在运行时须要在不一样的动态之间相互转换
  • 逆鳞:无
  • 相关设计模式
    • 策略模式:两者的实现方式很是类似,策略接口与状态接口,具体的策略与具体的状态以及两者都拥有的上下文,若是看它们的类图,会发现几乎如出一辙,而两者不一样的地方就在于,状态模式常常会在处理请求的过程当中更改上下文的状态,而策略模式只是按照不一样的算法处理算法逻辑,并且从实际场景来说,顾名思义,状态模式改变的是状态,策略模式改变的是策略

职责链模式(Chain of Responsibility Pattern 6-2)

  • 设计原则:遵循迪米特
  • 经常使用场景:一个请求的处理须要多个对象当中的一个或几个协做处理
  • 使用几率:15%
  • 复杂度:中
  • 变化点:处理链的长度与次序
  • 选择关键点:对于每一次请求是否每一个处理的对象都须要一次处理机会
  • 逆鳞:无

解释器模式

  • 设计原则:遵循单一职责
  • 经常使用场景:有一种语言被频繁的使用
  • 使用几率:0.00009%
  • 复杂度:中高
  • 变化点:语言的规则
  • 选择关键点:被频繁使用的语言是否可用文法表示
  • 逆鳞:语言的规则无限制增加或规则十分不稳定

中介者模式(Mediator Pattern 6-2)

  • 设计原则:遵循迪米特,破坏单一职责
  • 经常使用场景:一个系列的对象交互关系十分复杂
  • 使用几率:10%
  • 复杂度:中
  • 变化点:对象之间的交互
  • 选择关键点:复杂的交互关系是否有共性可被中介者承担
  • 逆鳞:中介者没法工做

访问者模式(Visitor Pattern 6-2)

  • 设计原则:遵循倾斜的开闭原则
  • 经常使用场景:做用于一个数据结构之上的操做常常变化
  • 使用几率:5%
  • 复杂度:高
  • 变化点:数据结构之上的操做
  • 选择关键点:数据结构是否稳定以及操做是否常常变化
  • 逆鳞:数据结构的不稳定

策略模式(Strategy 5-3)

  • 设计原则:遵循单一职责、依赖倒置、迪米特、开闭原则
  • 经常使用场景:算法或者策略须要常常替换
  • 使用几率:60%
  • 复杂度:中
  • 变化点:策略的种类
  • 选择关键点:客户端是否依赖于某一个或若干个具体的策略
  • 逆鳞:无

备忘录模式(Memento Pattern 6-3)

  • 设计原则:遵循迪米特、开闭原则
  • 经常使用场景:须要在对象的外部保存该对象的内部状态
  • 使用几率:5%
  • 复杂度:中
  • 变化点:无
  • 选择关键点:是否能够在必要的时候捕捉到对象的内部状态
  • 逆鳞:大对象的备份

迭代器模式( Iterator 5-3)

  • 设计原则:遵循迪米特
  • 经常使用场景:须要迭代访问一个聚合对象中的各个元素,且不暴露该聚合对象内部的表示
  • 使用几率:99.99999%
  • 复杂度:中
  • 变化点:聚合对象的种类
  • 选择关键点:客户端是否关心遍历的次序
  • 逆鳞:无
  • 相关设计模式
    • 访问者模式:两者都是迭代的访问一个聚合对象中的各个元素,不一样的是,访问者模式中,扩展开放的部分在做用于对象的操做上,而迭代器模式中,扩展开放的部分在聚合对象的种类上,并且两者的实现方式也有着很大的区别。

 

结束语

        以上即是24种设计模式的各个特色与部分模式的对比,若是总结的过程中有疏漏或是错误,请各位不吝赐教,LZ感激涕零。数据结构

        此外须要说明的是,上面的几率当中有的会出现99.99999%这样的数字,这是由于这个模式已经嵌入到JAVA类库或是咱们经常使用的开源框架当中(标注一下:LZ总结的主要针对WEB开发,android的开发LZ并未接触过,因此不包括在此列),好比迭代器模式,只要你使用过ArrayList或者HashSet,LZ就认为这个模式被使用。这个使用频率从某种意义上讲,能够认为是该模式的重要程度,固然因为这个频率是LZ制定的,因此仅表明我的观点。架构

        这里再针对设计模式的学习,给各位提一点点建议,仅供参考,请各位吸优排差,次序不分前后。框架

        一、以前说过,学习设计模式除了努力以外还要靠缘分,因此若是有设计模式当时怎么看都不明白,能够暂且放下,以后说不定哪天你忽然之间就明白了。(此话并不是虚言,LZ不少次的顿悟常发生在上厕所、洗澡、回家路上等一些学习以外的时候。)post

        二、对于已经在工做的人来讲,能够常思考一下,有没有哪一个设计模式能够改善现有的系统架构,但不要轻易付诸实践。学习

        三、学习设计模式以前,必定要先整明白UML类图,什么关联,依赖,聚合,组合等等都得搞明白儿的,不然学习起来也依然会很吃力。

        四、对于初学者,必定要在弄清楚标准的实现代码以后,写一个属于本身的例子,哪怕是比葫芦画瓢,而后仔细体会设计模式使用先后的差别,主要从扩展性和类(类包括客户端,而不只仅指设计模式中的角色)的职责两个方面。

        五、必定要将设计模式的变化点搞清楚,这点很是重要,甚至重要程度高于设计模式的场景、实现方式以及类和对象之间的耦合关系,不少时候,设计模式的滥用就是由于变化点没搞清楚,以致于该变化的没变化,不应变化的常常变化,增长系统的负担。

        六、设计模式不是一次性学习完就能够扔掉不看的东西,而是要常常回过头来看看,说不定每一次你都有不同的体会,并且通常状况下,这些体会会愈来愈深入,愈来愈透彻。

        七、若是可能的话,多研究一些开源框架,去找找它们里面的设计模式。

        LZ暂时也就只能想到这些,若是之后有想到再补充吧,各位若是有什么好的建议也能够与LZ分享一下。

        到此为止,整个设计模式系列就真真正正的完全结束了,当初写的时候也没想到本身能够真的坚持下来,虽然说整整26篇文章不算多,可是LZ确实花费了大量的时间和精力,值得欣慰的是LZ本人也获得了巨大的收获,不只仅是对设计模式的理解日益加深,并且还得了很多猿友的支持,让LZ对分享这一道路更加坚决。

        之后的编程之路还很长,对于LZ来讲,编程并不只仅是工做,而是一份事业,它给了LZ荣誉、金钱、成就感等等不少东西,但愿各位至少在年轻的时候不要被一些悲观化的观点所干扰,特别是对编程有着热爱的猿友们,极致才能成就大道,但凡在一个领域有所成就者,大都是钻研了数十年的成果。

        固然,人各有志,LZ没法左右他人,但LZ已经决定了本身的路,学火影里鸣人的一句话,“这就是个人忍道。”

相关文章
相关标签/搜索