一文读懂23种设计模式

文章目录

  • 代码质量的评判标准
  • 设计原则
    • 1、SOLID原则:SRP(Single Responsibility Principle)单一职责原则
    • 2、SOLID原则:OCP(Open-Closed Principle)开闭原则
    • 3、SOLID原则:LSP(Liskov Substitution Principle)里氏替换原则
    • 4、ISP(Interface Segregation Principle)接口隔离原则
    • 5、SOLID原则:DIP(Dependence Inversion Principle)依赖倒置原则
    • 6、KISS(Keep It Short and Simple)原则
    • 7、YAGNI(You Ain't Goona Need It)原则
    • 8、DRY(Don't Repeat Yourself)原则
    • 9、LOD原则
  • 设计模式三大类
    • 创建模式
      • 单例模式
      • 原型模式
      • 工厂模式
      • 抽象工厂模式
      • 建造者模式
    • 结构模式
      • 外观模式
      • 组合模式
      • 装饰器模式
      • 适配器模式
      • 享元模式
      • 代理模式
      • 桥接模式
    • 行为模式
      • 模版模式
      • 迭代器
      • 责任链模式
      • 策略模式
      • 状态模式
      • 备忘录模式
      • 中介者模式
      • 命令模式
      • 访问者模式
      • 观察者模式
      • 解释器模式

结合最近学习内容,分享设计模式相关总结,后面会结合java语言,给出每种设计模式的代码实现和案例分析。
这篇文章是对设计模式的综述,简单介绍23种设计模式的应用场景和优缺点;
为了提高编码能力和编码规范性,首先需要定义标准,什么样的代码算是规范的代码?

代码质量的评判标准

  • 如何评价代码质量的高低
    • 标准多,带有主观性,无法用单一的标准去评判,需要综各个维度;
  • 最常用的评价标准有哪些
    • 最常用:可维护性、可扩展性、可读性
    • 其他:灵活性、简洁性、可复用性、可测试性
  • 如何才能写出高质量的代码
    • 设计思想
    • 设计原则
    • 设计模式
    • 编码规范
    • 重构技巧

设计原则

1、SOLID原则:SRP(Single Responsibility Principle)单一职责原则

  • 概念:一个类只负责完成一个职责或者功能;
  • 意义:
    • 提高类的内聚性
    • 实现代码的高内聚、低耦合

2、SOLID原则:OCP(Open-Closed Principle)开闭原则

  • 软件实体应对扩展开放,而对修改关闭
  • 开闭原则不是完全杜绝修改,而是以最小修改代码的代价来完成新功能的开发
  • 如何实现?
    • 时刻具备扩展意识,封装意识和抽象意识
    • 常用来提高代码扩展性的方法:多态、依赖注入、基于接口非实现编程、大部分设计模式:装饰、策略、模版、职责链和状态

3、SOLID原则:LSP(Liskov Substitution Principle)里氏替换原则

  • 概念:子类对象能够替换程序中父类对象出现的任何地方,并且保证原来的逻辑行为不变及正确性不被破坏
  • 核心:“design by contract,按照协议来设计”,父类中定义了函数的约定(或协议),子类可以改变函数内部的实现逻辑,但不能改变函数原有的约定
  • 里氏替换原则VS多态
    • 里氏替换是一种设计原则,用来指导继承关系中子类该如何设计,子类设计要保证在替换父类的时候,不改变原程序的逻辑及不破坏原有程序的正确性
    • 多态是面向对象编程的一大特性,是面向对象编程的一种语法,是一种代码实现的思路

4、ISP(Interface Segregation Principle)接口隔离原则

  • 概念:客户端不应该强迫依赖它不需要的接口,其中“客户端”可以理解为接口的调用者或使用者
  • 核心:“接口”三种不同理解
    • 1.把“接口”理解为一组接口的集合
    • 2.把“接口”理解为单个接口或者函数
    • 3.把“接口”理解为OOP中的接口
  • 单一职责原则VS接口隔离原则
    • 单一职责原则针对的是模块、类、接口的设计
    • 接口隔离原则提供了一种判断接口职责是否单一的标准:通过调用者中如何使用来间接判定

5、SOLID原则:DIP(Dependence Inversion Principle)依赖倒置原则

  • 控制反转(IOC):一个比较笼统的设计思想,并不是一种具体的实现方式;
  • 抽象不应该依赖于细节,细节应该依赖于抽象
  • 依赖注入:和控制反转刚好相反,是一种具体的编码技巧
  • 依赖注入框架:通过依赖注入提供的扩展点,简单配置下所有需要的类及其类与类之间的依赖关系,就可以实现由框架自动创建对象,管理对象的生命周期,依赖注入等原本需要程序员做的事情

6、KISS(Keep It Short and Simple)原则

  • 概念:尽量保持简单‘
  • 意义:保持代码可读和可维护的重要手段
  • 如何满足KISS原则
    • 不要使用同事可能不懂的技术实现代码
    • 不要重复造轮子,善于使用已经有的工具类库
    • 不要过度优化

7、YAGNI(You Ain’t Goona Need It)原则

  • 概念:不要设计当前用不到的功能,不要去编写当前用不到的代码
  • 核心:不要过度设计

8、DRY(Don’t Repeat Yourself)原则

  • 概念:不要写重复的代码
  • 3中代码重复的情况
    • 实现逻辑重复
    • 功能语义重复
    • 代码执行重复
  • 提高代码复用性的手段
    • 减少代码耦合
    • 满足单一职责原则
    • 模块化
    • 业务与非业务逻辑分离
    • 通用代码下沉
    • 继承、多态、抽象、封装
    • 应用模版等设计模式
    • 有复用意识

9、LOD原则

  • 高内聚、松耦合
    • 高内聚:指导类本身的设计。松耦合:指导类与类依赖关系的设计
    • 高内聚:相近的功能应该放在同一个类中,不相近的功能不要放在同一个类中
    • 松耦合:即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类代码的改动
  • 迪米特法则
    • 概念:不该有直接依赖关系的类之间,不要有依赖;有依赖的类之间,尽量只依赖必要的接口
    • 意义:减少类之间的耦合,让类越独立越好

设计模式三大类

设计模式分为三种类型,共23类。
一文读懂23种设计模式_第1张图片

创建模式

单例模式

  • 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点
  • 主要解决:一个全局使用的类频繁的创建与销毁
  • 何时使用:想要控制实例的个数,节省系统资源
  • 如何解决:判断系统是否已经有这个实例,有则返回,没有则创建
  • 如何实现:
    • 懒汉模式
    • 饿汉模式
    • 双重检验锁
  • 应用场景:
    • 1、生成唯一序列号,流程单据号、人事编号
    • 2、WEB种的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来
    • 3、创建一个对象需要消耗的资源过多,比如I/O与数据库的连接等
  • 优缺点
    • 优点:1.在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例;2.避免对资源的过多占用(比如写文件操作)
    • 缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么实例化
  • 注意事项:getInstance()方法中需要使用同步锁synchronized(Singleton class)防止多线程同时进入噪声instance被多次实例化

原型模式

  • 意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
  • 主要解决:在运行期建立和删除原型
  • 何时使用:
    • 1.当一个系统应该独立于它的产品创建,构成和表示时
    • 2.当要实例化的类是在运行时刻指定时,例如:通过动态装载
    • 3.为了避免创建一个与产品层次平行的工厂类层次时
    • 4.当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些
  • 如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例
  • 如何实现:实现克隆操作,在java继承Cloneable,重写clone
  • 应用场景:细胞分裂、资源优化场景、游戏中的角色、武器属性等
  • 优缺点
    • 优点:1.性能提高;2.避免构造函数的约束
    • 缺点:对已有的类,配备克隆方法需要对类的功能进行通盘考虑;必须实现Cloneable接口
  • 注意事项:与通过对一个类进行实例化来构造新的对象不同的是,原型模式是通过拷贝一个现有对象生成新的对象的。浅拷贝实现Cloneable,重写,深拷贝是通过实现Serializable读取二进制流

工厂模式

  • 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
  • 主要解决:主要解决接口选择的问题
  • 何时使用:我们明确的计划不同条件下创建不同的实例时
  • 如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品
  • 如何实现:创建过程在其子类执行
  • 应用场景:
    • 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方
    • 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时
    • 3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口
    • 4、播放视频不需要考虑格式问题,自动解码
  • 优缺点
    • 优点:
      • 1.一个调用者想创建一个对象,只要知道其名称就可以了
      • 2.扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以
      • 3.屏蔽产品的具体实现,调用者只关心产品的接口
    • 缺点:每增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加系统的复杂度,同时也增加了系统具体类的依赖
  • 注意事项:复杂对象适合使用工厂模式,而简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式,如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度

抽象工厂模式

  • 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类
  • 主要解决:主要解决接口选择问题
  • 何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品
  • 如何解决:在一个产品族里,定义多个产品
  • 如何实现:在一个工厂里聚合多个同类产品
  • 应用场景:
    • 1、QQ换皮肤,一整套一起换
    • 2、生成不同操作系统的程序
  • 优缺点
    • 优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
    • 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的Creator里加代码,有要在具体的里面加代码
  • 注意事项:产品族难扩展,产品等级易扩展

建造者模式

  • 意图:将一个复杂的构建与其表示相分离,使得同样的构建构成可以创建不同的表示
  • 主要解决:“一个复杂对象”的创建工作
  • 何时使用:一些基本部件不会变,而其组合经常变化的时候
  • 如何解决:将变与不变分离开
  • 如何实现:
    • 建造者:创建和提供实例
    • 导演:管理建造出来的实例的依赖关系
  • 应用场景:
    • 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的“套餐”
    • 2、java中的StringBuilder
  • 优缺点
    • 优点:
      • 1.建造者独立,易扩展
      • 2.便于控制细节风险
    • 缺点:
      • 1.产品必须有共同点,范围有限制
      • 2.会有很多的建造类
  • 注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序

结构模式

外观模式

  • 意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
  • 主要解决:降低访问复杂系统内部子系统的复杂度,简化客户端之间的接口
  • 何时使用:
    • 客户端不需要知道系统内部的复杂联系,整个系统只需要提供一个“接待员”即可
    • 定义系统的入口
  • 如何解决:客户端不与系统耦合,外观类与系统耦合
  • 如何实现:在客户端和复杂系统之间再加上一层,这一层将调用顺序、依赖关系等处理好
  • 应用场景:
    • 1、商场的接待员
    • 2、Java的三层开发模式
    • 3、机场内的导航功能
    • 4、电子签功能,用户只需要点击页面的发送电子签按钮,系统将匹配模版生成电子签,发送邮件链接及验证码给签署方
  • 优缺点
    • 优点:
      • 1.减少系统互相依赖;
      • 2.提高灵活性、安全性
    • 缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适
  • 注意事项:在层次化结构中,可以使用外观模式定义系统中每一层的入口

组合模式

  • 意图:将对象组合成树形结构以表示“整体-部分”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
  • 主要解决:在树形结构的问题中,模糊简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦
  • 何时使用:具有树形结构的对象
  • 如何解决:树枝和叶子实现统一接口,树枝内部组合改接口
  • 如何实现:树枝内部组合改接口,并且具有内部属性list,里面放component
  • 应用场景:集团的组织结构、树形菜单、文件、文件夹的管理
  • 优缺点
    • 优点:高层模块调用简单、节点自由增加
    • 缺点:组合模式,叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则
  • 注意事项:定义时为具体类

装饰器模式

  • 意图:动态的给一个对象添加额外的职责,对增加功能来说,装饰器模式比生成子类更为灵活
  • 主要解决:通过继承方式扩展一个类,随着扩展功能的增多,子类会很膨胀,解决继承方式的这一弊端
  • 何时使用:在不想增多很多子类的情况下扩展类
  • 如何解决:将具体功能职责划分,同时继承装饰者模式
  • 如何实现:
    • 1.Component类充当抽象角色,不应该具体实现
    • 2.修饰类引用和继承Component类,具体扩展类重写父类方法
    • 双重检验锁
  • 应用场景:
    • 1、扩展一个类额外功能
    • 2、动态增加功能,动态撤销
    • 3、创建一个对象需要消耗的资源过多,比如I/O与数据库的连接等
  • 优缺点
    • 优点:装饰类和被装饰类可以独立发展,不会互相耦合,装饰者模式是继承的一个代替模式,装饰模式可以动态扩展一个实现类的功能
    • 缺点:多层装饰比较复杂
  • 注意事项:可代替继承

适配器模式

  • 意图:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类一起工作
  • 主要解决:主要解决在软件系统中,常常要将一些“现存的对象”放到新的环境中,而新的环境要求的接口是对现对象不能满足的
  • 何时使用:
    • 系统需要使用现有的类,而此类的接口不符合系统的需要
    • 通过接口转换,将一个类插入另一个类之中
  • 如何解决:继承或依赖(推荐)
  • 如何实现:适配器继承或依赖已有的对象,实现想要的目标接口
  • 应用场景:
    • 1、Java中的JDBC
    • 2、有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式
  • 优缺点
    • 优点:
      • 1.可以让任何两个没有关联的类一起运行
      • 2.提高了类的复用、透明度以及灵活性
    • 缺点:
      • 1.过多地使用适配器,会让系统非常凌乱,不易进行整体把握
      • 2.由于Java至多继承一个类,所以至多只能适配一个适配者类,而且目标必须是抽象类
  • 注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目问题

享元模式

  • 意图:使用共享技术有效地支持大量细粒度的对象
  • 主要解决:在有大量对象时,有可能会造成内存溢出,我们把其公共的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建
  • 何时使用:
    • 系统中有大量的对象,并且消耗大量的内存
    • 这些对象的状态大部分可以外部化
    • 系统不依赖于这些对象身份,这些对是不可分辨的
  • 如何解决:用唯一标识码判断,如果内存中有,则返回这个唯一标识码所标识的对象
  • 如何实现:用HashMap存储这些对象
  • 应用场景:
    • 1、Java的String,如果有则返回,如果没有则创建一个字符串缓存池里面
    • 2、数据库连接池
  • 优缺点
    • 优点:大大减少对象的创建,降低系统的内存,使效率提高
    • 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固优化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱
  • 注意事项:
    • 注意划分外部状态和内部状态,否则可能会引起线程安全问题
    • 这些类必须有一个工厂对象加以控制

代理模式

  • 意图:为其他对象提供一种代理以控制对这个对象的访问
  • 主要解决:在直接访问对象时带来的问题,如性能问题、安全问题
  • 何时使用:想在访问一个类时做一些控制
  • 如何解决:增加中间层
  • 如何实现:实现与被代理类组合
  • 应用场景:
    • 1、Windows里面的快捷键
    • 2、火车票代售点
    • 3、Spring aop
  • 优缺点
    • 优点:职责清晰、高扩展性、智能化
    • 缺点:
      • 可能会早生请求处理速度变慢
      • 实现代理模式需要额外的工作,有的代理模式的实现非常复杂
  • 注意事项:
    • 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变代理类的接口
    • 和装饰器模式的区别:装饰器是为了增强功能,而代理模式是为了加以控制

桥接模式

  • 意图:将抽象部分与实现部分分离,使他们都可以独立的变化
  • 主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活
  • 何时使用:实现系统可能有多个角度分类每一种角度都可能变化
  • 如何解决:把这种多角度分类分离出来,使他们独立变化,减少它们之间的耦合
  • 如何实现:抽象类依赖实现类
  • 使用场景
    • 路由器的桥接模式
    • 跨平台视频播放器,平台和视频格式两个独立变化的纬度
  • 优缺点
    • 优点:
      • 抽象与实现的分离
      • 优秀的扩展能力
      • 实现细节对客户透明
    • 缺点:增大了系统的理解与设计维度
  • 注意事项:对于两个独立变化的维度,使用桥接模式再合适不过了

行为模式

模版模式

  • 意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
  • 主要解决:一些方法通用,却在每一个子类都重写了这一方法
  • 何时使用:有一些通用的方法
  • 如何解决:将这些通用的算法抽象出来
  • 如何实现:在抽象类实现,其他步骤在子类实现
  • 使用场景:电子签
  • 优缺点:
    • 优点:
      • 封装不变的部分,扩展可变的部分
      • 提供公共代码,以便于维护
    • 缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
  • 注意事项:为防止恶意操作,一般模版方法都加上final关键字

迭代器

  • 意图:提供一种方法顺序访问一个联合对象中各个元素,而又无须暴露该对象的内部表示
  • 主要解决:不同的方式来遍历整个聚合对象
  • 何时使用:需要遍历一个聚合对象
  • 如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象
  • 如何实现:定义接口:hasNext,next
  • 使用场景:Java中的iterator
  • 优缺点:
    • 优点:简化了对聚合对象的遍历
    • 缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性
  • 注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样可以做到不暴露集合的内部结构,又可以让外部代码透明的访问集合内部的数据

责任链模式

  • 意图:避免请求发送者与接受者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理为止。
  • 主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了
  • 何时使用:在处理消息的时候要过滤很多道
  • 如何解决:拦截的类都实现统一接口
  • 如何实现:Handler里面聚合它自己,在HandlerRequest里判断是否合适,如果没达到条件则向下传递,向谁传递之前set进去
  • 使用场景:
    • 拦截器
    • 工作流审批办理
  • 优缺点:
    • 优点:
      • 降低请求的发送者和接受者的耦合度
      • 简化了对象,使得对象不需要知道链的结构
      • 增强给对象指派职责的灵活性
      • 增加新的请求处理类很方便
    • 缺点:
      • 不能保证请求一定被接收
      • 对系统性能有一定影响,也有可能造成循环调用
  • 注意事项:在JAVA WEB中遇到很多应该用

策略模式

  • 意图:定义一系列的算法,把他们一个个封装起来,并且使他们可互相替换
  • 主要解决:在有多种算法相似的情况下,使用if…else所带来的复杂和难以维护
  • 何时使用:一个系统有许多类,而区分他们的只是他们直接的行为
  • 如何解决:将这些算法封装成一个一个的类,任意的替换
  • 如何实现:实现同一个接口
  • 使用场景:
    • 旅游的出行方式,每一种都是一个策略
    • 12306的某一列车的选票
  • 优缺点:
    • 优点:
      • 算法可以自由切换
      • 避免使用多重条件判断
      • 扩展性良好
    • 缺点:
      • 策略类会增多
      • 所有策略类都需要对外暴露
  • 注意事项:如果一个系统的策略类多于4个,就需要考虑使用混合模式,解决策略类膨胀的问题

状态模式

  • 意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了他的类
  • 主要解决:对象的行为依赖于他的状态(属性),并可以根据他的状态改变而改变他的相关行为
  • 何时使用:代码中包含大量与对象状态有关的条件语句
  • 如何解决:将各种具体的状态抽象出来
  • 如何实现:
  • 使用场景:
    • 行为随状态改变而改变的场景
    • 条件、分支语句的代替者
  • 优缺点:
    • 优点:
      • 封装了转换原则
      • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
      • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
    • 缺点:
      • 状态模式的使用必然会增加系统类和对象的个数
      • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
  • 注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过5个

备忘录模式

  • 意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在改对象之外保存这个状态
  • 主要解决:实现恢复、撤销操作
  • 何时使用:需要做恢复、撤销操作等功能时
  • 如何解决:通过一个备忘录类专门存储对象状态
  • 如何实现:客户不与备忘录类耦合,与备忘录管理类耦合
  • 使用场景:
    • 游戏存档
    • 数据的事务管理
    • Windows的系统还原
  • 优缺点:
    • 优点:
      • 给用户提供一个可恢复状态的机制,能够回到某个历史的状态
      • 实现信息的封装,使得用户不需要关系状态的保存细节
    • 缺点:保存数据会占用较大资源以及内存
  • 注意事项:
    • 为了符合迪米特原则,还要增加一个管理备忘录的类
    • 为了节约内存,可使用原型模式+备忘录模式

中介者模式

  • 意图:用一个中介对象来封装一系列的对象交互,中介者使各个对象不需要显示地互相引用,从而使其松耦合,而且可以独立地改变他们之间的交互
  • 主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变的复杂,同时若一个对象发生变化,我们也需要跟踪与之相关联的对象,同时做出响应的处理
  • 何时使用:多个类相互耦合,形成了网状结构
  • 如何解决:将上述网状结后分离为星型结构
  • 如何实现:对象Colleague之间的通信封装到一个类中单独处理
  • 使用场景:
    • 机场调度系统
    • MVC框架,C是M和V的中介者
  • 优缺点:
    • 优点:
      • 降低了类的复杂度,将一对多转化为一对一
      • 各个类之间的解耦
      • 符合迪米特原则
    • 缺点:中介者会庞大,变得复杂难以维护
  • 注意事项:不应当在职责混乱的时候使用

命令模式

  • 意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化
  • 主要解决:需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适
  • 何时使用:在某些场合,需要对行为进行记录、撤销/重做、事务等处理时
  • 如何解决:通过调用者调用接受者执行命令,顺序:调用者—>命令—>接受者
  • 如何实现:定义三个角色:
    • received真正的命令执行对象
    • Command
    • invoke使用命令对象的入口
  • 使用场景:认为是命令的地方都可以使用命令模式,比如GUI中每一个按钮都是一条命令
  • 优缺点:
    • 优点:
      • 降低系统的耦合度
      • 新的命令可以很容易添加到系统中去
    • 缺点:使用命令模式可能会导致某些系统有过多的具体命令类
  • 注意事项:系统需要支持命令的撤销操作和恢复操作,也可以考虑使用命令模式

访问者模式

  • 意图:主要讲数据结构与数据操作分离
  • 主要解决:稳定的数据结构和易变的操作耦合问题
  • 何时使用:需要对一个对象结构中的对象进行很多不同且不相关的操作,而需要避免让这些操作污染这些对象的类,使用访问者模式将这些封装到类中
  • 如何解决:在被访问的类里面加一个对外提供接待访问者的接口
  • 如何实现:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者
  • 使用场景:记者当事人的访问,这就是访问者模式
  • 优缺点:
    • 优点:
    • 符合单一职责原则
    • 优秀的灵活性、扩展性
  • 缺点:
    • 具体元素对访问者公布细节,违反了迪米特原则
    • 具体元素变更比较困难
    • 违反了依赖倒置原则,依赖了具体类,没有依赖抽象
  • 注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器

观察者模式

  • 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并自动更新
  • 主要解决:一个对象状态改变给其他对象通知的具体问题,而且要考虑到易用和低耦合,保证高度的协作
  • 何时使用:需要进行类似广播通知时
  • 如何解决:使用面向对象技术,可以将这种依赖关系弱化
  • 如何实现:在抽象类里面有一个ArrayList存放观察者们
  • 使用场景:
    • 线上拍卖,拍卖师观察最高价,然后通知给其他竞价者竞价
    • 工组流中的监听器
  • 优缺点:
    • 优点:
      • 观察者和被观察者都是抽象耦合的
      • 建立一套触发机制
    • 缺点:
      • 被观察者对象有过多的直接或间接的观察者,通知观察者将会花费很多时间
      • 如果观察者与被观察者存在循环依赖,则可能会导致系统崩溃
      • 观察者模式只是通知到被观察者的状态的改变,不能给观察者通知被观察者的变化过程
  • 注意事项:
    • Java中已经有了对观察者模式的支持类
    • 避免循环引用
    • 如果顺序执行,某一观察者错误会导致系统的卡壳,一般采用异步方式

解释器模式

  • 意图:给一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用改标识来解释语言中的句子
  • 主要解决:对于一些固定文法构建一个解释句子的解释器
  • 何时使用:特定类型的问题发生的频率足够高时,就值得构建一个解释器来解释改问题
  • 如何解决:构建语法树,定义终极表达式与非终极表达式
  • 如何实现:构建环境类、包含解释器之外的一些全局信息,一般就是HashMap
  • 使用场景:
    • 编译器
    • SQL解析
    • 运算表达式计算
  • 优缺点:
    • 优点:
      • 可扩展性好、灵活
      • 易于实现简单文法
    • 缺点:
      • 可利用场景比较少
      • 复杂的文法比较难以维护
      • 解释器模式会引起类膨胀
      • 解释器模式采用递归调用方法
  • 注意事项:可利用场景比较少,Java中如果碰到可以用expression4J代替

你可能感兴趣的:(设计模式,设计模式,java,开发语言)