各种设计模式的简单介绍

设计模式的六大原则

  • 单一职责原则
  • 里氏替换原则
  • 依赖倒置原则
  • 接口隔离原则
  • 迪米特原则
  • 开闭原则

设计模式的分类

创建型模式

创建型模式:对对象实例化的抽象,通过采用抽象类所定义的接口,封装了系统中对象如何创建,组合等信息。包括以下几种设计模式

抽象工厂模式

优点
  • 分离了具体类
  • 更容易在产品系列中进行转换
  • 提高了产品间一致性
缺点
  • 难以支持新的产品等级结构
  • 支持新的产品等级结构就要扩展原来的抽象工厂接口
适用场景
  • 系统独立于产品的创建,组成以及表示
  • 系统配置成具有多个产品的系列
  • 当要强调一系列相关的产品对象的设计便于进行联合使用时
  • 当提供一个产品类库,而只想显示它们的接口而不是实现时
应用举例

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

构建器模式

优点
  • 产品的内部表象可以独立变化
  • 将构建代码与表示代码相分离,可以使客户端不必知道产品内部组成的细节
缺点
  • 构建器模式创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间差异性很大。则不适合使用构建器模式
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
适用场景

创建复杂对象的算法独立于组成对象的部分以及这些部分的集合方式

构造过程必须允许已构建对象有不同表示

应用举例

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

工厂方法模式

定义一个用于创建对象(产品)的接口,由子类决定实例化那个类的对象( 产品)

优点
  • 没有了将应用程序绑定到代码中的要求,代码只处理接口,因此可以使用任何实现了接口的类
  • 允许子类提供对象的扩展版本
  • 符合迪米特法则,依赖倒置原则,里氏替换原则
缺点
  • 需要Creator和相应的子类作为工厂方法的载体,如果应用模型确实需要creator的子类存在,则很好,否则需要增加一个类层次
适用场景
  • 当一个类不知道他所创建的产品的具体是那个子类时
  • 当创建对象的过程希望延缓到子类中进行时
  • 类希望子类指定他要创建的对象

应用举例

Windows的COM组件与MFC

原型模式

指定创建对象的种类,并且通过拷贝这些原型创建新的对象,以一个已有的对象作为原型,通过他来创建新对象

优点
  • 可以在允许时添加或删除产品
  • 原型模式提供简化的创建结构
  • 具有给一个应用软件加载新功能的能力
  • 产品类不需要非得由任何实现确定的等级结构
缺点

每一个类必须配备一个克隆方法

适用场景
  • 在运行时,指定需要实例化的类,例如动态载入,避免构建与产品的类层次结构相似的共产类层次结构
  • 当类的实例是仅有的一些不同状态组合

单例模式

一个类只有一个实例

优点
  • 对单个实例的受控制访问
  • 名称空间减少
  • 允许改进操作和表示
  • 允许可变数目的实例
  • 比类操作更灵活
缺点

单例类的扩展有很大困难,且职责过重,这在一定程度上违背了单一职责原则

适用场景

只有一个类实例

应用举例

逐渐生成器

结构型模式

主要用于如何组合已有的类和对象以获得更大的结构,它采用继承机制组合接口来实现,以提供 统一的外部试图或新的功能

适配器模式

将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本的接口不兼容而不能一起工作的那些类可以一起工作

优点
  • 允许两个或多个不兼容的对象进行交互和通信
  • 提高已有功能的重复适用
  • 增加了类的透明度
  • 灵活性
缺点

过多的适用适配器会让系统非常凌乱,不易整体进行把握

适用场景
  • 要使用已有的类,而该类接口与所需的接口并不匹配
  • 要创建可重复使用的类,该类可以与不相干或未知类进行协助,即类之间不需要兼容接口
  • 要在一个不同于已知接口的接口环境重使用对象
  • 必须要进行多个源之间的接口转换的时候
应用举例

在.Net重有一个类库已经实现的,非常重要的适配器——Data Adapter

桥接模式

将一个辅助的组件分成两个独立的但又相关的继承层次结构

优点
  • 可以将接口与是实现相分离
  • 提高可扩展性
  • 对客户端隐藏了实现细节
缺点
  • 增加了系统的理解和设计难度
  • 要求正确的识别出系统重两个独立变化的维度,因此其使用范围有一定的局限性
适用场景
  • 想避免在抽象及其实现之间存在永久的绑定
  • 抽象及其实现可以使用子类进行扩展
  • 抽象的实现被改动应该对客户端没有影响

组合模式

将对象组合成树型结构以表示“部分-整体”的层次结构

优点
  • 清楚的定义分层次的复杂对象,表示对象的全部或部分层次
  • 使得增加新构件 更容易了了
  • 提高了结构的灵活性和可管理的接口
缺点

使设计变得更加抽象。如果对象的业务规则很复杂,则实现组合模式具有很大的挑战性,而且不是所有的方法都与叶子对象子类有关联

适用场景
  • 想表示对象的部分–整体层次结构
  • 希望用户忽略组合对象与单个对象的不同,用户将统一的使用组合结构重的所有对象
  • 结构可以具有任何级别的复杂性,而且是动态的
应用举例

部分、整体场景如树型菜单,文件,文件夹的管理

装饰模式

动态的给对象添加一些额外的责任,就增加功能来说,装饰比生成子类更为灵活

优点
  • 比静态继承具有更大的灵活性
  • 简化代码
  • 改进对象的扩展性,用户可以通过编写新的类来做出改变
  • 装饰类和被装饰类可以独立发展,不会相互耦合
缺点

多层装饰比较复杂

适用场景
  • 想要在单个对象动态并且透明的添加责任,而这样不会影响其他对象
  • 想要在以后可能要修改的对象重添加责任
  • 当无法通过静态子类化实现扩展时

外观模式

为子系统重的一组接口提供一个统一的接口,外观模式通过一个高层接口隔离了外部系统与子系统间复杂的交互过程,使得复杂系统的子系统更容易使用

优点
  • 在不减少系统所提供的选项的情况下,为复杂系统提供了简单的接口
  • 客户端屏蔽了子系统组件
  • 提高了子系统和其客户端之间的弱耦合度
  • 将客户端请求转发后能够处理这些请求的子系统
缺点
  • 不能很好的限制客户使用子系统类,如果对客户访问子系统做太多的限制,则减少了可变性和灵活性
  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了开闭原则
适用场景
  • 为一个复杂子系统提供一个简单接口时
  • 客户程序与抽象类的实现之间存在着很大的依赖性
  • 构建一个层次结构的子系统时,适用Facade模式定义子系统中每层的入口点,如果子系统之间是相互依赖的,你可以让他们仅通过Facade进行通信,从而简化了他们之间的依赖关系

享元模式

通过共享对象减少系统中低等级的,详细的对象数目

优点
  • 减少了要处理的对象数目
  • 如果对象能够持续,可以减少内存和存储设备
缺点
  • 使得应用程序在某种程度上来说更加复杂化了
  • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长
适用场景
  • 应用程序使用大量的对象
  • 由于对象数目巨大,导致很高的存储开销
  • 应用程序不依赖于对象的身份

代理模式

为控制初始对象的访问,提供了一个代理或者占位符对象

优点
  • 远程代理可以隐藏对象位于不同的地址空间的事实
  • 虚拟代理可以执行优化操作
缺点
  • 请求的处理速度变慢
  • 实现代理模式需要额外的工作,从而增加了系统实现的复杂度
适用场景
  • 当需要为了一个对象在不同的地址空间提供局部的代表时
  • 当需要创建开销非常大的对象时
应用举例

防火墙代理—保护目标不让恶意用户靠近

行为型模型

从大量的实际行动中概括出来作为行为的理论抽象、基本框架和标准,该类模式主要用于对象之间的职责及其提供的服务的分配,他不仅描述对象或类的模式,还描述它们之间的通信模式

责任链模式

在系统中建立一个链,消息可以首先接收到他的级别被处理,或者定位到可以处理他的对象

优点
  • 降低耦合度
  • 增加向对象指定责任的灵活性
适用场景
  • 多个对象可以处理一个请求,而其处理器却是未知的
  • 可以动态指定能够处理请求的对象集

命令模式

在对象中封装请求,保存命令并传递给方法以及任何其他对象一样返回该命令

优点
  • 添加新命令不用修改已有类
  • 将操作对象与知道如何完成操作的对象相分离
适用场景
  • 想要通过要执行的动作来参数化对象
  • 在不同的时间指定、排序以及执行请求

解释器模式

解释定义其语法表示的语言

优点
  • 容易修改并扩展语法
  • 容易实现语法
适用场景
  • 语言的语法比较简单
  • 效率并不是最主要的问题

迭代器模式

提供一种方法顺序的访问一个聚合对象中的各个元素,而又不暴露改对象的内部表示

优点
  • 支持集合的不同遍历
  • 简化集合接口
适用场景
  • 在不开放集合内部对象表示的前提下访问集合对象内容
  • 支持记号个对象的多重遍历
  • 为遍历集合中的不同结构提供了统一的接口

备忘录模式

保持对象状态的“快照”,在不向外界公开其内容的情况下返回到它的最初状态

优点
  • 保持封装完整
  • 简化了返回到初始状态所需的操作
适用场景
  • 必须保存对象的快照
  • 适用直接接口来获得状态有可能会公开对象的实现细节,从而破坏对象的封装性

观察者模式

为组件向相关接收方广播消息提供了灵活的方法

优点
  • 抽象了主体与Observer 之间的耦合关系
  • 支持广播方式的通信

适用场景

  • 对一个对象的修改涉及其他对象的修改,而不知道有多少对象需要修改
  • 对象应该能在不设置对象标识的情况下通知其他对象

状态模式

允许对象在内部状态变化时变更其行为,并且修改其类

优点
  • 定位指定状态的行为,并根据不同的状态来划分行为
适用场景
  • 对象行为依赖于其状态,且对象必须在运行时根据其状态修改行为
  • 操作具有大量以及多部份组成的取决于对象状态的条件语句

策略模式

定义了一组用来表示可能行为集合的类

优点
  • 另一种子类化方法
  • 在类自身中定义行为,减少条件语句
  • 更容易扩展模型
适用场景
  • 许多相关类只是在行文方面有所区别
  • 需要算法的不同变体
  • 算法使用客户端未知的数据

模板方法模式

提供了在不重写方法的前提下允许子类重载部分方法的方法

优点
  • 代码使用
适用场景
  • 想要一次实现算法的不变部分,适用子类实现算法的可变行为
  • 子类间通用行为需要分解,定位到通用类,可以避免代码重复问题

访问者模式

提供了一种方便的,可维护的方法来表示在对象结构元素上进行的操作。改模式允许在不改变操作元素的类的前提下定义一个新操作

优点
  • 容易添加新操作
  • 集中相关操作并且排除不相关操作
适用场景
  • 定义对象结构的类很少被修改,但想要在此结构上定义新的操作
  • 对象结构包含许多具有不同接口的对象类,并且想要对这些依赖于具体的类的对象进行操作

中介者模式

通过引入一个能够管理对象间消息分布的对象,简化系统中对象的通信

优点
  • 取出对象间的影响
  • 简化对象间的协议
  • 集中化控制
适用场景
  • 对象集合需要一个定义但复杂的方式进行通信
  • 想要在不使用子类的情况下自定义分布在几个对象间的行为

本文由博客一文多发平台
OpenWrite 发布!

    原文作者:赵镇
    原文地址: https://segmentfault.com/a/1190000020874735
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞