概述

常用设计模式可以概括为23种,按照特点可以将其分为三大类型

  • 创建型:主要用来创建对象的模型,抽象了实例化的过程,创建型设计模式有俩个主要功能:
    • 将系统所使用的具体类的信息封装起来
    • 隐藏类的实例是如何被创建和组织的,外界对于这些对象只知道它们共同的接口
    • 常见的创建型设计模式有:
      • 单例模式:一个类只实例化一个对象,整个系统只使用该实例对象
      • 工厂方法模式:工厂类成为抽象类,实例的创建工作将由其具体的子类完成;用意:定义工厂接口,实际产品由其子类进行,强调“单个对象“的变化
      • 抽象工厂模式:抽象工厂是所有工厂模式中的再次抽象,在不必指定具体产品类型的情况下,创建多个产品族中的产品对象,强调“系列对象”的变化
      • 建造者模式:构造对象实例逻辑外移(移到类的外部),强调将一个复制对象的构建过程进行分离,强调的是产品的构建过程
      • 原型模式:与工厂模式一样,隐藏了对象的创建,但是与创建新对象不同的是,原型模式是通过复制一个现有对象生成新对象
  • 结构型:结构型模式讨论的是类和对象的结构,它采用继承机制来组合接口和实现(类结构型模式)或者通过组合一些对象实现新的功能(对象结构型模型)
    • 常见的结构型设计模式有:
      • 代理模式:为其他对象提供一种代理以控制对改对象的访问
      • 装饰模式:动态地给一个对象添加一些额外的职务,就添加功能而言,装饰模式比生成子类更为灵活
      • 适配器模式:将一个类的接口变换成客户端所期待的另一个接口,使原本因接口不匹配而无法一起工作的俩个类能一起工作
      • 组合模式:也叫合成模式,将对象组合成树形结构以表示“部分--整体”的层次结构,使得单个对象和组合对象的使用具有一致性
      • 桥接模式:也叫桥梁模式,将抽象和实现解耦,使得俩者可独立变化
      • 门面模式:也叫外观模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,提供一个高层次的接口,使得子系统更易于使用
      • 亨元模式:池技术的重要实现,使用共享对象可有效地支持大量细粒度的对象
  • 行为型:关注对象的行为,用来解决对象之间的联系问题
    • 常见的行为型设计模式有:
      • 模板方法模式:定义一个操作中的算法的框架,将其一些步骤延迟到子类中,使得子类可以不改变一些算法的结构即可重定义该算法的特定步骤
      • 命令模式:一种高内聚模式,将一个请求封装成一个对象,从而使得不同的请求把客户端参数化,对请求排队或者请求请求日志,可以提供命令的撤销和回复功能
      • 责任链模式:使多个对象都有机会处理请求,从而避免请求的发起者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止
      • 策略模式:定义一组算法,将每个算法都封装起来,使得他们之间可以相互切换
      • 迭代器模式:提供一种方法访问一个容器中的各个元素,而又不暴露该对象的内部细节
      • 中介者模式:用一个中介对象封装一系列的对象交互,使各对象不需要显式的相互作用,从而使其松耦合,并可独立改变它们之间的交互
      • 观察者模式:也叫发布-订阅模式,定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖它的对象都会得到通知并自动更新
      • 备忘录模式:不破坏对象封装性,捕获对象内部状态,并在对象外部保存这个状态
      • 访问者模式:封装一些作用于某种数据结构的各个元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
      • 状态模式:当一个对象内在状态改变时允许其改变行为,改对象看起来改变了其类型,状态模式其核心是封装,状态的变更引起行为的变更
      • 解释器模式:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该文法表示来解释语言中的句子

设计原则

  • 单一职责原则:一个类,应当只有一个引起它变化的原因;即一个类应该只有一个职责
    • 为何需要单一职责原则?
      • 一个庞大的对象承担了太多的职责,当客户端需要该对象的某一个职责时,就不得不将所有的职责都包含进来,从而造成冗余代码。
    • 单一职责是否要求一个类只定义一个职责?
      • 单一职责原则并不是极端地要求我们只能为类定义一个职责,而是利用极端的表述方式重点强调,在定义对象职责时,必须考虑职责与对象之间的所属关系。职责必须恰如其分地表现对象的行为,而不至于破坏和谐与平衡的美感,甚至格格不入。
    • ■ 降低类的复杂性;■ 提高类的可读性;■ 提高代码的可维护性和复用性;■ 降低因变更引起的风险
  • 里式替换原则:所有引用基类的地方必须能透明地使用其子类对象。清晰明确地说明只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道父类还是子类;但是反过来则不可以,有子类的地方,父类未必就能适应。
    • 按照里氏替换原则,当多个类之间存在继承关系时,通常应该使用父类或接口来指向子类的对象(除非需要使用子类特有的方法),这更利于提高系统的可扩展性
  • 依赖倒置原则:依赖倒置原则在Java语言中的表现是:■ 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生;■ 接口或抽象类不依赖于实现类;■ 实现类依赖于接口或抽象类。
  • 接口隔离原则:只提供调用者需要的方法,屏蔽不需要的方法
  • 迪米特法则:迪米特法则又叫最少知识原则(Least Knowledge Principle,LKP),意思是一个对象应当对其他对象尽可能少的了解
  • 开闭原则:一个软件实体应当对扩展开放,对修改关闭
    • 在面向对象的编程中,开闭原则是最基础的原则,起到总的指导作用,其他原则(单一职责、里氏替换、依赖倒置、接口隔离、迪米特法则)都是开闭原则的具体形态

创建型模式

单例模式

单例模式::确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例类中一个最重要的特点是类的构造函数是私有的,从而避免外界利用构造函数直接创建出任意多的实例。另外需要注意的是,由于构造函数是私有的,因此该类不能被继承

■ 饿汉式单例类类加载时,就进行对象实例化;■ 懒汉式单例类:第一次引用类时,才进行对象实例化。

单例模式的缺点:■ 单例模式无法创建子类,扩展困难,若要扩展,除了修改代码基本上没有第二种途径可以实现;■ 如果采用单例模式的类没有完成,是不能进行测试的;单例模式的类通常不会实现接口,这也妨碍了使用mock的方式虚拟一个对象。

根据功能,单例类可以分为有状态单例类和无状态单例类。■ 有状态单例类:一个有状态单例类的对象一般是可变的,通常当做状态库使用。例如,给系统提供一个唯一的序列号。■ 无状态单例类:无状态单例类是不变的,通常用来提供工具性的功能方法。例如, IO或数据库访问等。

单例类具有状态,所以在使用时应注意以下两点。■ 单例类仅局限于一个JVM,因此当多个JVM的分布式系统时,这个单例类就会在多个JVM中被实例化,造成多个单例对象的出现。如果是无状态的单例类,则没有问题,因为这些单例对象是没有区别的。如果是有状态的单例类,则会出现问题,例如,给系统提供一个唯一的序列号,此时序列号不唯一,可能出现多次。因此,在任何使用EJB、RMI和JINI技术的分布式系统中,应当避免使用有状态的单例类。■ 同一个JVM中会有多个类加载器,当两个类加载器同时加载同一个类时,会出现两个实例,此时也应尽量避免使用有状态的单例类。

工厂方法模式

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪个类,工厂方法使一个类的实例化延迟到其子类中

工厂方法模式中,涉及四个角色

  • 抽象工厂角色:该角色是工厂方法模式的核心,与应用系统无关,任何创建对象的工厂类必须实现该接口
  • 具体工厂角色:该角色实现抽象工厂角色,含有与应用密切相关的逻辑,并且受应用程序的调用以创建产品对象
  • 抽象产品角色:该角色负责定义产品的共性,实现对产品最抽象的定义
  • 具体产品角色:该角色实现抽象产品角色所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例

图片说明

图片说明

抽象工厂模式

抽象工厂模式:抽象工厂模式是工厂方法模式的升级版本。在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

抽象工厂模式是工厂方法模式的进一步抽象,针对的是一族产品。如果产品族中只有一种产品,则抽象工厂模式就退化为工厂方法模式。除了工厂方法模式的优点外,抽象工厂模式还具有下列优点。■ 产品族内的约束为非公开状态,在不同的工厂中,各种产品可能具有不同的相互依赖关系,这些依赖关系由工厂封装在其内部,对于工厂的使用者是不可见的。■ 生产线的扩展非常容易,如果要针对同一产品族建立新的生产线,只需要实现产品族中的所有产品接口并建立新的工厂类即可。

image-20210429145301529