软件设计模式复习精要

快查清单

角色数量模式名称
1单例模式
2外观、原型、模板方法和职责链模式
3适配器、组合、代理、策略、状态、备忘录模式
4工厂方法、抽象工厂、建造者、桥接、装饰、享元、观察者、中介者、迭代器、命令和解释器模式
5访问者模式

在这里插入图片描述

概述

软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。

软件设计模式的目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。

根据其目的(模式是用来做什么的)可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三种。

•创建型模式(Creational Pattern)主要用于创建对象,它关心的是对象的创建过程。

•结构型模式(Structural Pattern)主要用于处理类或对象的组合。

•行为型模式(Behavioral Pattern)主要用于描述对类或对象怎样交互和怎样分配职责。

简单了解的模式

抽象工厂(Abstract Factory Pattern)

模式动机

在工厂方法模式中,具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性。一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是,有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。例如,不同厂家都生产电视、空调和洗衣机。

img

模式定义

抽象工厂模式又称为Kit模式,属于对象创建型模式。抽象工厂模式(Abstract Factory Pattern)提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

注意:工厂方法模式是类创建型模式。

模式结构与角色分析

抽象工厂模式(用来生产不同产品族的全部产品,针对增加新的产品,无能为力,支持增加产品族)

在这里插入图片描述

原型模式(Prototype Pattern)

模式动机

有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式**通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。**这就是原型模式的动机。

模式定义

它是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式允许一个对象再创建另外一个可定制的对象,无需知道任何创建的细节。

模式结构与角色分析

工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝原型自己来实现创建过程。

在这里插入图片描述

角色1:抽象原型类Prototype,定义抽象方法clone()。

角色2:具体原型类ConcretePrototype,重写抽象方法clone()。

要点:客户端Client关联Prototype,调用抽象方法clone()。

很多软件提供的复制、粘贴功能都是原型模式的应用。

原型模式(Prototype)通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。

所有的Java类都继承自java.lang.Object,而Object类提供一个克隆方法clone(),可以将一个Java对象复制一份。

指定某个类实现接口Cloneable(它没有定义任何方法),标识这个类支持复制。

使用接口Cloneable方法clone()【专用方式】复制比使用运算符new【通用方式】具有更高的执行效率和更好的扩展性。专用方式的UML类图如下:

在这里插入图片描述

模式评价

  1. 当创建新的对象实例较为复杂时,使用原型模式可以简化的对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
  2. 原型模式允许动态增加或减少产品类。
  3. 原型模式具有给一个应用软件动态加载新功能的能力。
  4. 产品类不需要非得有任何事先确定的等级结构。

享元模式(Flyweight Pattern)

模式动机

享元模式对那些通常因为数量太大而难以用对象来表示的概念或实体进行建模。

例如:

考虑生活中使用共享单车的情形。一个城市,在没有共享单车前,每人有一辆自行车,则会造成城市拥挤。如今,出现了mobike(摩拜)和hellobike(哈啰)等移动交通运营商(平台)。有了共享单车,可以节约城市资源,减少拥挤。

在进行共享单车平台软件设计时,按照单车的种类创建对象,需要使用享元模式,以减少创建对象的数量和内存消耗。

如为字母表中的每一个字符创建一个享元,每个享元对象存储一个字符代码。逻辑上,文档中的给定字符每次出现都有一个对象与其对应,然而物理上相同的字符共享一个享元对象,而这个对象可以出现在文档结构的不同地方。

由于不同的字符对象数远小于文档中的字符数,因此,对象的总数远小于一个 初次执行的程序所使用的对象数目。对于一个所有字符都使用同样的字体和颜 色的文档而言,不管这个文档有多长,只需要分配100个左右的字符对象(大 约是ASCII字符集的数目)。由于大多数文档使用的字体颜色的组合不超过10 种,实际应用中这一数目不会明显增加。因此,对单个字符进行对象抽象是具有实际意义的。

模式定义

运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很近,状态变化很小,对象使用次数增多。享元模式是一种对象结构型模式

内部状态与外部状态

实现了享元模式共享的目的,无论几个用户,运行游戏一样,就只保留一个游戏逻辑代码。但是,这些用户毕竟不是同一个用户,用户名不同,用户的牌也不同。我们称享元对象内部不随环境变化的共享部分称为内部状态,而随环境而改变、不可以共享的状态称为外部状态

模式结构与角色分析

在这里插入图片描述

角色1:抽象享元类Flyweight,通常是一个接口或抽象类,声明向外界提供对象内部状态(intrinsicState)的公共方法、设置外部状态(extrinsicState)。

角色2:具体享元类ConcreteFlyweight,内部状态可共享时设计为具体享元类。

角色3:非共享具体享元类UnsharedConcreteFlyweight,内部状态不能被共享时设计为非共享具体享元类。UnsharedConcreteFlyweight需要同时处理内部状态和外部状态

角色4:享元工厂类FlyweightFactory,用于创建并管理享元对象,享元池一般设计成键值对。

角色5:客户类Client,

模式评价

享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:享元模式使得系统更加复杂。

为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

适用情形

当以下所有的条件都满足时,可以考虑使用享元模式:

  1. 一个系统有大量的对象,造成耗费大量的内存。
  2. 这些对象的状态中的大部分都可以外部化。
  3. 这些对象可以按照内部状态分成很多的组,当把外部对象从对象中剔除时,每一个组都可以用相对较少的共享对象代替。
  4. 软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。

模式应用

享元模式在编辑器软件中大量使用,如在一个文档中多次出现相同的图片,则只需要创建一个图片对象,通过在应用程序中设置该图片出现的位置,可以实现该图片在不同地方多次重复显示。

  • §Windows DLL(动态链接库)
  • String类型

访问者模式(Visitor Pattern)

模式动机

  • 对于系统中的某些对象,它们存储在同一个集合中,且具有不同的类型。对于该对象集合中的对象,可以接受一类称为访问者的对象来访问。不同的访问者形成一个访问者集合,每个访问者对对象集合里元素(对象)的访问方式有所不同。即同一访问者对相同的元素对象可能存在多种不同的操作方式。
  • 实际开发中,访问者对元素的操作方式并不稳定,可能还需要增加新的操作,以满足新的业务需求。
  • 访问者模式就是一个值得考虑的解决方案。

模式定义

表示一个作用于某对象结构中的各个元素的操作。访问者模式使得用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下, 定义作用于这些元素新操作。

要点:

  1. 对象结构存储了不同类型的元素(对象),以供不同的访问者访问。
  2. 同一访问者可以用不同的方式访问不同的元素,同一元素可以接受不同的访问者以不同的方式访问。
  3. 新增访问操作不需要修改原有类,系统具有良好的扩展性。

模式结构与角色分析

在这里插入图片描述

角色1:抽象访问者Visitor。

角色2:具体访问者ConcreteVisitor。

角色3:元素Element,定义了抽象方法accept()

角色4:具体元素ConcreteElement,除了重写抽象方法accept()外,还定义了普通方法operation()。

角色5:对象结构ObjectStructure,是元素的集合。

模式评价

优点:

  • 增加操作的灵活性:可以在不修改对象结构的前提下,增加新的操作。
  • 分离关注点:操作和数据结构分离,符合单一职责原则。
  • 容易扩展:增加新的访问者只需要添加新的访问者类,不需要修改已有的元素类。

缺点:

  • 对象结构变化困难:如果对象结构频繁变化,维护会很麻烦,因为每次变化都需要修改所有的访问者。
  • 违背开闭原则:尽管增加新的操作很方便,但增加新的元素类型却不那么容易,因为需要修改所有的访问者。

适用情形

  1. 当需要对一个对象结构中的对象进行很多不同且不相关的操作,而又不希望这些操作“污染”这些对象的类时。
  2. 适用于一个相对稳定的数据结构,并且操作变化频繁的系统。
  3. 对象结构包含许多类,并且它们存在不同的接口,且希望对这些类进行很多不同的操作。

模式应用

在这里插入图片描述

解释器模式(Interpreter Pattern)

模式动机

解释器模式主要用于使用面向对象语言开发的编译器和解释器设计,描述如何构成一个简单的语言解释器。

模式定义

定义语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”意思是使用规定格式和语法的代码。

解释器模式是一种类行为型模式

模式结构与角色分析

在这里插入图片描述

角色1:环境类Context聚合定义了需要解释的抽象语法树。

角色2:抽象表达式类AbstractExpression,定义了Context类型参数的抽象方法interpret(Context)。

角色3:终结表达式类TerminalExpression,作为AbstractExpression的子类,需要重写方法interpret(Context)。

角色4:非终结表达式NonterminalExpression,作为AbstractExpression的子类,聚合父类AbstractExpression,需要重写方法interpret(Context)。

要点:

(1)TerminalExpression表示一个常量或简单变量。

(2)NonterminalExpression用来表示运算符,本例是二元运算符。因此,应有left和right。

(3)解释器方法interpret(Context)一致地解释TerminalExpression和NonterminalExpression

(4)客户端需要同时创建Context对象与AbstractExpression对象。

模式评价

  • 模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。模板方法模式的优势是:在子类定义详细的处理算法时不会改变算法的结构。
  • 模板方法是一种代码复用的基本技术,它们在类库中尤为重要,它们提取了类库中的公共行为。
  • 模板方法导致一种反向的控制结构,一个父类调用一个子类的操作,而不是相反。

模式应用

当我们需要开发一种新的语言时,可以考虑使用解释器模式。

**解释器(Interpreter)**的目标是理解指令并执行。

**编译器(Compiler)**的目标是编译程序为机器代码。

引例:解释含有不同优先级运算符的表达式 1+2*3-32/2^3 的过程(即计算 步骤),可通过构建一个二叉树来说明。

在这里插入图片描述

运算数作为树的叶子结点,运算符作为树(或子树)的根结点。对于左、右子树而言,优先级较高的运算符位于树的较低层。

后序遍历(左-右-根)结果:1 2 3 * + 32 2 3 ^ / -

使用二叉树的后序遍历结果并按运算符的语义递归计算出表达式的值,即解释整棵树的根结点就可以计算出表达式的值。

一般掌握的模式(简答题)

工厂方法模式Factory Method Pattern

模式动机

在简单工厂模式中,如果需要增加新的产品类型,那么需要修改工厂类的代码,这就使得整个设计在一定程度上违反了“开放封闭原则”。我们定义一个抽象工厂类,并在具体工厂类里重写这个抽象按钮工厂类中定义的抽象方法。抽象化的结果使这种结构可以在不修改已有具体工厂类的情况下引进新的产品。

模式定义

工厂方法模式(FactoryMethod Pattern)定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。

在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。

工厂方法模式属于类创建型模式

模式结构与角色分析

在这里插入图片描述

模式评价

工厂方法模式:在简单工厂模式的基础上,对工厂也进行了抽象。(用来生产同一等级结构中的固定产品,支持增加任意产品)

符合开闭原则,但增加了很多类。

工厂方法模式(Factory Method Pattern)又称为工厂模式,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。

* 把简单工厂类(可能业务代码庞大)拆分成了一个个的工厂类,这些工厂类都实现于同一个抽象接口。

* 工厂方法模式假定:一个具体工厂只生产一种电视产品

* 当增加电视产品时,只需修改配置文件,无需修改程序。因此,工厂方法模式符合符合OCP原则。

建造者模式(Builder Pattern)

模式动机

在某些情况下,一个对象会有一些重要的属性,在它们没有恰当的值之前,对象不能作为一个完整的产品使用。比如,一个电子邮件有发件人地址、收件人地址、主题、内容、附录等部分,而在最起码的收件人地址未被赋值之前,这个电子邮件不能发出。

模式定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步地创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式

建造者模式分离了对象子组件的单独建造(由Builder负责)和装配(由Director负责),从而可以建造出复杂的对象。

建造者模式实现了建造和装配的解耦。相同的建造器、不同的装配方式,可以建造出不同的对象。不同的建造器、相同的装配方式,可以建造出不同的产品对象。

使用建造者模式,能很好地实现代码复用。

要点:

(1)创建者模式将复杂产品的构建与它的表示分离,使得同样的构建过程可以创建不同的产品表示。

(2)Director用它来控制建造过程,也用它来隔离用户建造过程之间的关联。建造者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象。

(3)实际应用本模式时,还需要在ConcreteBuilder里定义与Product相同的字段,以便返回完整的产品。

定义和使用钩子方法,可以控制产品部件的构建的实现。

在建造者模式中,当只有一个建造者时,不必使用指挥者和抽象的建造者,并可使用流式写法实现复杂产品的构建。

模式结构及角色分析

角色1:复杂产品类Product,定义了组成字段及其setter/getter方法,是一个实体类。

角色2:抽象建造者类Builder,定义了产品部件构建获取完整产品的抽象方法。

角色3:具体建造者类ConcreteBuilder重写了基类建造复杂产品部件的抽象方法,定义了获得完整产品的普通方法,与Product类形成创建依赖关系。

角色4:指挥者类Director聚合了抽象类Builder对象,定义了控制复杂产品构建的方法。

模式应用

麦当劳快餐肯德基快餐,它们均由食品和饮料两部分组成。

在这里插入图片描述

其中,KBuilder是具体建造者,表示肯德基快餐。另一个具体建造者是麦当劳快餐MBuilder,没有画出。

组合模式(Composite Pattern)

模式动机

树形结构在软件开发中随处可见,如操作系统的文件目录结构、应用软件中的菜单结构和公司组织机构等。

模式定义

组合模式(CompositePattern)定义:组合多个对象形成树形结构以表示具有部分—整体关系的层次结构。组合模式让客户端可以统一地对待单个对象和组合对象。

组合模式又称部分—整体(Part—Whole)模式,它将对象组织到树形结构中,用以描述整体与部分的关系。

组合模式是对象结构型模式

组合模式根据抽象构件的定义形式,可划分为透明组合模式安全组合模式

要点:

组合模式用于处理树型结构,包含Component(抽象构件)、Leaf(叶子构件)和Composite(容器构件)三个角色。

组合模式描述了如何将容器对象和叶子对象进行递归组合。

通过抽象机制,可以一致地对待容器对象和叶子对象。

模式结构与角色分析

在这里插入图片描述

代理模式(Proxy Pattern)

模式动机

一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,去掉客户不能看到的内容和服务或者增添客户需要的额外服务。例如,在网页上查看一张图片,由于网速等原因图片不能立即显示,我们可以在图片传输过程中先把一些简单的用于描述图片的文字传输到客户端,此时这些文字就成为了图片的代理。

模式定义

给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy或Surrogate,它是一种对象结构型模式

模式结构与角色分析

在这里插入图片描述

代理模式的一个主要功能是通过代理来控制对实际对象的访问。通过抽象层,代理类可以在实际对象前拦截调用,进行预处理(如权限检查、延迟加载等)或后处理(如结果缓存、日志记录等),而不需要修改实际对象的代码。在代理模式中,客户端不必直接依赖于具体实现类,即隔离客户端和真实对象。如果需要更换实际对象,只需要更改代理类中的实现,而不需要修改客户端代码。

角色1:抽象主题Subject,声明了真实主题和代理主题的共同抽象类(或接口),这样一来在任何使用真实主题的地方都可以使用代理主题。该抽象类定义一个抽象的请求方法request()。

角色2:真实主题RealSubject,定义了代理角色所代表的真实对象。继承Subject,重写方法request()。

角色3:代理Proxy,内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理角色与真实主题角色具有相同的接口,以便可以在任何时候都可以替代真实主题;控制对真实主题的应用,负责在需要的时候创建真实主题对象(和删除真实主题对象)。Proxy关联RealSubject,并通过RealSubject对象执行方法request()。

模式评价

优点:

代理模式能够协调调用者和被调用者,能够在一定程度上降低系统的耦合度。

当对象在远程机器上,要通过网络来生成时速度可能会很慢,此时应用代理模式可以掩盖对象在网络上生成的过程,系统的速度会加快。

对于大对象的加载(如大图片),虚拟代理可以让加载过程在后台执行,前台使用的代理对象会使得整体运行速度得到优化。

缺点:

请求的处理速度会变慢,并且实现代理模式需要额外的工作。

代理模式分为静态代理动态分理

AOP(Aspect-Oriented Programming)是一种编程范式,旨在增强软件系统的模块性、可维护性和可重用性。它通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,以便将它们集中处理,而不是分散在整个代码中。

横切关注点是指那些存在于应用程序各个模块中、跨越多个模块的功能或需求,例如日志记录、安全性、事务管理、性能监控等。这些关注点横跨多个对象和类,因此会导致代码的重复性和散乱性。

AOP通过将横切关注点抽象为称为切面(aspects)的模块化单元来解决这个问题。切面是一组与特定关注点相关的行为,它定义了在应用程序执行过程中在何处和何时插入这些行为。

AOP的核心思想是将切面与主要业务逻辑进行解耦。它通过使用一些概念和技术,如切点(pointcuts)、通知(advice)和织入(weaving),在运行时动态地将切面的行为织入到目标对象的执行过程中。

切点定义了在应用程序执行过程中哪些位置应该插入切面的行为。通知定义了在切点位置插入的具体行为,可以是前置通知(before advice)、后置通知(after advice)、环绕通知(around advice)等。织入是将切面的代码插入到目标对象的过程,可以通过编译时织入、类加载时织入或运行时织入来实现。

远程代理为一个位于不同的地址空间的对象提供一个局域代表对象。可以隐藏一个对象存在于不同地址空间的事实。可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部分的网络通信工作。客户端专注于业务逻辑。

要点:静态代理中的RealSubject和Proxy有共同的父类(或实现相同的接口)。

代理类只是在一定程度上代表了原来的实现类。动态代理和静态代理一样,都是对目标方法进行增强,而且让增强的动作和目标动作分开,达到解耦的目的。但是,在动态代理模式中,真实主题类与代理类不是实现于同一个接口。

动态代理有JDK动态代理CGLib动态代理远程代理等形式。

适配器模式(Adapter Pattern)

模式定义

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。

适配器模式可分为类结构型模式对象结构型模式两种。

模式结构与角色分析

适配器模式包含如下三个角色。

角色1:抽象目标Target

角色2:适配者类Adaptee,Adaptee表示被适配的对象。

角色3:适配器类Adapter。

根据使用方式,可划分为对象适配器和类适配器两种。对象适配器使用组合或聚合等关联(委托)方式,类适配器使用类继承方式。

在类适配器模式中,Adapter需要继承Adaptee。由于Java只支持单继承,因此,Target只能是接口。

桥接模式(Bridge Pattern)

模式动机

当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口,而具体的子类则用不同方式加以实现。但是,此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。

模式定义

桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handleand Body)模式或接口(Interface)模式。

模式名称:桥接模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”。这句话有三个关键词:抽象化、实现化、脱耦

模式结构与角色分析

在这里插入图片描述

角色1:实现类接口Implementor,定义抽象方法implementorImp()。

角色2:抽象类Abstraction,定义了抽象方法operation(),聚合了Implementor。

角色3:具体实现类ConcreteImplementor:作为Implementor的实现类。

角色4:扩充抽象类RefinedAbstraction:作为Abstraction的子类。

在桥接模式中,不仅Implementor具有变化(ConcreateImplementior),而且Abstraction也可以发生变化(RefinedAbstraction),这是一个多对多的关系,而且两者的变化是完全独立的。

RefinedAbstraction与ConcreateImplementior之间是松散耦合,仅仅通过Abstraction与Implementor之间的聚合关系联系起来。

知识扩展点:根据类变化的属性,抽象形成接口(或抽象类),实现多个维度的独立化。
在桥接模式中不仅Implementor具有变化(ConcreateImplementior),而且Abstraction也可以发生变化(RefinedAbstraction),这是一个多对多的关系,而且两者的变化是完全独立的。RefinedAbstraction与ConcreateImplementior之间松散耦合,它们仅仅通过Abstraction与Implementor之间的聚合关系联系起来。

在桥接模式中不仅Implementor具有变化(ConcreateImplementior),而且Abstraction也可以发生变化(RefinedAbstraction),这是一个多对多的关系,而且两者的变化是完全独立的。RefinedAbstraction与ConcreateImplementior之间松散耦合,它们仅仅通过Abstraction与Implementor之间的聚合关系联系起来。

模式应用

【简明示例】设计一个图形类库,其中包含多种图形,比如圆形、矩形等,每种图形可以设置不同的颜色。

抽象部分使用一个抽象类Shape,定义所有图形的抽象方法draw(),并包含一个指向实现部分Color的引用。

实现部分使用一个接口 Color,定义应用颜色的抽象方法applyColor(),并具有两个具体实现类Red和Blue,分别实现了不同的颜色方案。

结论:应用桥接模式,分离图形和颜色两个部分,便于代码复用和扩展。

外观模式(Facade Pattern)

模式定义

外观模式:一个子系统的外部与其内部的通信,必须通过一个统一的外观对象进行。

如同医院的接待员一样,外观模式的外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。

模式结构与角色分析

在这里插入图片描述

解释器模式(Interpretor Pattern)

模式动机

解释器模式主要用于使用面向对象语言开发的编译器和解释器设计,描述如何构成一个简单的语言解释器。

当我们需要开发一种新的语言时,可以考虑使用解释器模式。

**解释器(Interpreter)**的目标是理解指令并执行。

**编译器(Compiler)**的目标是编译程序为机器代码。

模式定义

定义语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”意思是使用规定格式和语法的代码。

解释器模式是一种类行为型模式

模式结构与角色分析

在这里插入图片描述

角色1:环境类Context聚合定义了需要解释的抽象语法树。

角色2:抽象表达式类AbstractExpression,定义了Context类型参数的抽象方法interpret(Context)。

角色3:终结表达式类TerminalExpression,作为AbstractExpression的子类,需要重写方法interpret(Context)。

角色4:非终结表达式NonterminalExpression,作为AbstractExpression的子类,聚合父类AbstractExpression,需要重写方法interpret(Context)。

要点:

(1)TerminalExpression表示一个常量或简单变量。

(2)NonterminalExpression用来表示运算符,本例是二元运算符。因此,应有left和right。

(3)解释器方法interpret(Context)一致地解释TerminalExpression和NonterminalExpression

(4)客户端需要同时创建Context对象与AbstractExpression对象。

模式评价

  • 模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。模板方法模式的优势是:在子类定义详细的处理算法时不会改变算法的结构。
  • 模板方法是一种代码复用的基本技术,它们在类库中尤为重要,它们提取了类库中的公共行为。
  • 模板方法导致一种反向的控制结构,一个父类调用一个子类的操作,而不是相反。

中介者模式(Mediator Pattern)

模式动机

当对象之间存在复杂交互的系统中,需要加入一个中介者,大家都与中介者交流,听从中介者安排

机场调度中心

•如果没有机场调度中心,每一个飞机需要自己看有没有飞机和自己一起抵达,有没有空跑道降落,场面异常混乱。

•有了机场调度中心,飞机听从安排就可以了。

QQ服务器

•如果没有QQ服务器,消息在网络中可以用广播发送,每个客户既是客户端,又是服务器端,这使用客户之间的关系异常复杂。

•如果有了QQ服务器,张三发给李四的过程是:张三发消息,服务器收到消息,查找李四,发消息给李四,通知张三,消息已经送达。

在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其呈现单一的职责。

中介者模式:简化了对象之间的交互实现了同事之间的解耦**,同事之间的交互由中介者负责,**同事只需考虑自身的业务逻辑。

对于一个软件模块,可能由很多对象构成,而且这些对象之间可能存在相互的引用。为了减少对象两两之间复杂的引用关系,使之成为一个松耦合的系统,我们需要使用中介者模式,这就是中介者模式的模式动机。

模式定义

中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。

模式结构与角色分析

在这里插入图片描述

角色1:抽象中介者Mediator。

角色2:抽象同事类Colleague,关联Mediator。

角色3:具体同事类ConcreteColleague。

角色4:具体中介者ConcreteMediator,关联ConcreteColleague。

模式评价

优点:

  • 简化了对象之间的交互。
  • 将各同事解耦。
  • 减少子类生成。
  • 可以简化各同事类的设计和实现。

缺点:

  • 在具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。

适用情形

  1. 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
  2. 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。
  3. 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。可以通过引入中介者类来实现,在中介者中定义对象交互的公共行为,如果需要改变行为则可以增加新的中介者类。

模式应用

在中介者模式中,通过创造出一个中介者对象,将系统中有关的对象所引用的其他对象数目减少到最少,使得一个对象与其同事之间的相互作用被这个对象与中介者对象之间的相互作用所取代。因此,中介者模式就是迪米特法则的一个典型应用

策略模式(Strategy Pattern)

模式动机

  • 完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略(Strategy),我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。

  • 在软件开发中也常常遇到类似的情况,实现某一个功能有多个途径。此时,需要使用一种设计模式来使得系统可以灵活地选择解决途径,也能够方便地增加新的解决途径。

  • 在软件系统中,有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法。当然,也可以将这些查找算法封装在一个统一的方法中,通过if…else…等条件判断语句来进行选择。

  • 为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法。在这里,每一个封装算法的类我们都可以称之为策略。为了保证这些策略的一致性,一般会用一个抽象策略来做算法的定义,而具体每种算法则对应于一个具体策略

模式定义

定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。

策略模式对应于解决某个问题的一个算法簇,用户可以任选一个算法解决问题。同时,可以增加算法或替换某个算法。策略模式是一种对象行为型模式

模式结构与角色分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

角色1:抽象策略Strategy,通常为接口或抽象类,为所有支持的算法声明了抽象方法。

角色2:具体策略ConcreteStrategy,实现了抽象策略中声明的算法。

角色3:环境类Context,聚合了抽象策略,并提供了通过聚合的对象使用具体策略的方法。上下文对象的行为会随着具体策略对象的改变而改变。

策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决问题。同时,可以方便的更换算法或者增加新的算法,并由客户端决定调用哪个算法。

模式评价

  • 策略模式是对“开闭原则”的完美体现,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 策略模式提供了管理相关的算法族的办法。
  • 策略模式提供了可以替换继承关系的办法。
  • 使用策略模式可以避免使用多重条件转移语句。
  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以让一个对象在许多行为中动态地选择一种行为。
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • 一个系统需要动态地在几种算法中选择一种。
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。
  • 在微软公司提供的演示项目PetShop 4.0中使用策略模式来处理同步订单和异步订单的问题。
  • 在BLL中的Order类里通过反射机制从配置文件中读取策略配置的信息以决定到底是使用哪种订单处理方式。只需要修改配置文件即可更改订单处理方式,提高了系统的灵活性。

Java接口回调

Java接口回调可以被视为一种应用了策略模式的设计方式,使得代码更具扩展性和可变性。Android 的事件处理机制,使用了Java接口回调。例如,类View包含单击事件处理的内部接口OnClickListener及其方法onClick(View v),通过应用方法setOnClickListener(OnClickListener o)实现在单击对象v后执行对象o实现的接口方法代码。

命令模式(Command Partten)

模式动机

【引例1】

考虑生活中使用空调的例子。早期的空调,没有遥控器,用户直接操作空调上的相关按钮实现想要的操作。有了遥控器后,通过它发送操作命令,如模式指令(如制冷、制热、除湿等),空调通过红外接收或蓝牙等技术接收到指令后,便完成相应的操作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【引例2】

去烧烤店吃烧烤, 如果客人直接和烧烤的伙计打交道, 则烧烤的伙计将直接面对各种各样的客户各种各样的需求,各种各样的口味, 而且有的客人会因为等不及而走掉,但是伙计却不知道…最后烧烤伙计手忙脚乱。

如果增加一个服务员,负责接待客户,那么客人将向服务员点菜;服务员手中拿着烧烤订单纸,客人只要把自己想要的东西写上,并注明需要的口味,服务员需按客人点餐的顺序把订单放在烧烤伙计的窗台上;如果有客人退单了,那么她只需要把订单抽掉就行了; 烧烤伙计则只需要看订单安心做烧烤就行了。

Command是抽象命令;LightCommand是具体命令;Light是命令接收者;

Control是调用者,聚合Command,其方法setCommand()调用接口方法。

Client使用Control之前(属于关联关系),需要明确控制的对象。因此,Client与LightCommand依赖关系。

好处:新增设备时,只需要增加新的具体命令及对应的接收者,而不需要修改已有的类,即这种方式实现了调用者与接收者之间的解耦。

应用到软件开发:在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体请求的接收者即可,此时,可以使用命令模式来进行设计,使得请求的发送者与接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。

模式定义

命令模式:将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

模式结构与角色分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

角色1:抽象命令类Command

角色2:请求调用者Invoker,聚合Command,作为请求发送者,它通过命令对象来执行请求。

角色3:具体命令者ConcreteCommand

角色4:请求接收者Receiver,执行与请求相关的操作(请求的业务处理)。

使用:客户端先分别创建Receiver和Invoker对象,然后创建一个具体命令对象并设定它的接收者,最后使用Invoker的发送命令方法call()。

命令模式vs备忘录模式

命令模式(Command Pattern)和备忘录模式(Memento Pattern)都可以用于实现撤销(Undo)和重做(Redo)功能,但它们的实现方式和设计目的有所不同。命令模式和备忘录模式的区别在于它们的设计思想和实现方式。命令模式将每个操作封装成命令对象,并通过命令历史记录来实现撤销和重做;而备忘录模式则是通过保存和还原对象的状态来实现撤销和重做。

联用命令模式和组合模式实现宏命令

宏命令又称组合命令,它是命令模式与组合模式联合使用的产物。宏命令也是一个具体命令,只是它包含了对其他命令对象的引用。一个宏命令的成员对象可以是简单命令,也可以是宏命令。因此,在调用宏命令的execute()方法时,会递归调用它所包含的每个成员命令的execute()方法,从而实现对命令的批处理。

Chain of Responsibility

模式动机

  • 职责链可以是一条直线、一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。

  • 链上的每一个对象都是请求处理者,职责链模式可以将请求的处理者组织成一条链,并使请求沿着链传递,由链上的处理者对请求进行相应的处理。

  • 客户端无须关心请求的处理细节以及请求的传递,只需将请求发送到链上即可,将请求的发送者和请求的处理者解耦。这就是职责链模式的模式动机。

模式定义

职责链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式又称为责任链模式,它是一种对象行为型模式

要点:

(1)Handler通过定义自身类型的对象successor作为对接替者的引用而形成处理者链(自关联、自身聚合)。

(2)ConcreteHandler处理请求之前,先要查看是否有处理权限。权限就处理,没有就将请求转发至后继者。

(3)请求发出者并不知道链上的哪个对象是请求处理的终结者,这使得可以动态地重新组织链和分配职责。

(4)职责链并非由职责链模式负责创建,而是由系统的其他部分来完成,一般是在使用职责链的客户端中创建职责链。职责链模式降低了请求发送端和接收端之间的耦合,使得多个对象都有机会处理这个请求。

(5)实际应用上,通常使用一个类来表示请求。显然,请求类并非是职责链模式的角色。

模式结构与角色分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

角色1:抽象处理者Handler,一般设计为抽象类,定义了抽象方法handRequest()。同时,定义了一个Handler类型的对象successor(接替者)。

角色2:具体处理者ConcreteHandler,是Handler的子类,重写处理请求的抽象方法handRequest()。

模式应用

逐级批假。

适用情形

在以下情况下可以使用职责链模式

  1. 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 可动态指定一组对象处理请求

纯的职责链模式和不纯的职责链模式

职责链模式分为纯的职责链模式和不纯的职责链模式两种。

  • 一个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个:一个是承担责任,另一个是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又将责任向下传的情况。
  • 在一个纯的职责链模式里面,一个请求最终必须被某一个处理者对象所接收。
  • 在一个不纯的职责链模式里面,一个请求可以最终不被任何接收端对象所接收。

职责链模式和状态模式比较

职责链模式:当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHander对象负责处理它。链中的对象自己并不知道链的结构,在运行时确定。

**职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用或指针,而不需要保持它所有的候选接受者。这也就大大降低了耦合度了。**也就是说,我们可以随时地增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性。

**状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。**把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。

状态模式定义新的子类可以很容易地增加新的状态和转换,其目的是为了消除庞大的条件分支语句。

状态模式状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。

请假例子,找班长请假,班长只能请半天,否则班长向老师申请,如果请假时间超过一周,老师要跟副年级主任请示,如果请假超出一个月,主任要跟年级正主任请示,然后被批准,或不被批准。

问题:如果班长请假了,用状态模式,其他学生都请不了假了,也就是如果状态模式中任何一环缺失的话,这个事件都无法进行下去。

职责链模式的链式在客户端连接的,也就是说,如果我们请假,请假制度一旦改变,比如说我们不需要班长,或者是先请求老师后直接请求主任或者中间多了一个环节,都是很容易实现的,所以,职责链模式要比状态模式灵活很多。

职责链模式与状态模式的最大的不同是设置自己的下一级的问题上,状态模式是在类的设计阶段就定好的,不能在客户端改变,而职责链的下一级是在客户端自己来确定的。

职责链模式注重职责的传递,由客户端配置;状态模式注重对象状态的转换,转换过程对客户端是透明的。

职责链模式灵活应对变化

项目初期,用户可以到银行存钱(人民币)

系统开发完毕,随着社会进步

有客户要存取美元、日元……

建立职责链,第一个类处理人民币,如果是其他币种,交给后面的类处理。

项目初期,用户可以到银行存钱(人民币)

系统开发完毕,随着社会进步

有客户要存取美元、日元……

建立职责链,第一个类处理人民币,如果是其他币种,交给后面的类处理。

重点掌握的模式(编程题)

单例模式(Singleton Partten)

模式动机

对于系统中的某些类来说,有且只能有一个实例。例如:

  1. 一个系统只能有一个窗口管理器。
  2. 系统中可以有许多打印机,但是只能有一个打印机正在工作。

问题:我们怎样保证一个类只有一个实例并且这个实例易于被访问呢?

模式定义

让类自身负责保存它的唯一实例。这个类可以保证没有其它实例被创建,并且它可以提供一个访问该实例的方法。

  • 定义私有、静态、自身类型的字段 uniqueInstance
  • 定义私有构造方法 Singleton()
  • 定义了方法返回值为Singleton的公有、静态方法 getInstance()
img

单例模式—饿汉式和懒汉式

1.核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

2.应用场景:Windows中的任务管理器;数据库连接池;项目中的配置文件类等等。

3.优点:减少了系统性能开销。

4.五种单例模式:

​ ①饿汉式(线程安全,调用效率高。但是,不能延时加载)

​ 类初始化时立即加载,类初始化时,处于天然的线程安全模式,因此线程安全,方法没有同步,效率高。

​ ②懒汉式(单例对象延迟加载)

​ 真正用的时候才加载,资源利用率高了,但每次调用getInstance()方法都要同步,并发效率低。

​ ③双重检测锁

img

​ 将同步内容放到if内部,提高了执行效率,不必每次获取对象都进行同步,第一次创建之后就不需要在外等待了。

​ 看起来不错,但是由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题,不建议使用

​ ④ 静态内部类实现方式(也是懒加载)

img

​ 外部类没有static属性,则不会像饿汉式那样立即加载对象,只有真正调用getInstance(),才会加载静态内部类。加载 类时是线程安全的,兼并了并发高效调用和延迟加载的优势。

​ ⑤使用枚举实现单例

img img

​ 实现较简单,枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞。

​ 缺点是无延迟加载。

代码

 /*
 * 单例类的构造函数为私有,在类的外部无法使用new创建其实例;
 * 提供一个自身的静态私有成员变量;
 * 提供一个公有的静态方法,用于获取单例类的实例对象。
 */

class Singleton {  //单例类
    //自身类型的静态成员(类属性)——自关联
    private static Singleton instance;
    //定义无参构造方法为私有,保证在类外不能创建该类的实例
    
    private Singleton() {
       System.out.println("执行了构造方法。");
    }
    
    //公有的静态方法getInstance()用于获取单例类的实例
    public static Singleton getInstance() {
       if(instance==null) {
          instance=new Singleton();  //实例化
          System.out.println("第一次创建实例!");
       } else {
          System.out.println("不再新建对象,直接使用原来的实例!");
       }
       return instance;  //返回对象
    }
}

public class Client {   //测试类
    public static void main(String args[]) {
       Singleton singleton1,singleton2;  //声明对象
       singleton1=Singleton.getInstance(); //对象实例化
       System.out.println("singleton1引用的对象:"+singleton1);
       System.out.println("singleton1引用的对象的Hash码:"+singleton1.hashCode());
       System.out.println("==================================================");
       singleton2=Singleton.getInstance();
       System.out.println("singleton2引用的对象:"+singleton2);
       System.out.println("singleton2引用的对象的Hash码:"+singleton2.hashCode());
       System.out.println("两个引用的对象在内存中的id相同,因此,它们的Hash码相同。反之不然。");
    }
}

装饰模式(Decorator Partten)

模式定义

装饰模式:不改变原有对象的前提下,动态地给一个对象增加一些额外的功能。

**img

**

装饰类聚合了父类对象,让方法的调用具有了层次关系。

  1. 具体装饰类,由于继承了父类,里面都有一个抽象组件对象。

  2. 原本他们之间没有任何关系,当相互装饰的时候才产生了关系,也就是存在对象的引用。

  3. 每个类对象其实做的事情很简单,就是先调用一下父类方法,再调用一下自己的方法。

  4. 装饰的先后顺序, 决定了对象方法的调用顺序。

优点:

  • 比继承更加灵活:不同于在编译期起作用的继承;装饰者模式可以在运行时扩展一个对象的功能。另外,也可以通过配置文件在运行时选择不同的装饰器,从而实现不同的行为。也可以通过不同的组合,可以实现不同效果。
  • 符合“开闭原则”:装饰者和被装饰者可以独立变化。用户可以根据需要增加新的装饰类,在使用时再对其进行组合,原有代码无须改变。

缺点:

  • 装饰者模式需要创建一些具体装饰类,会增加系统的复杂度。

重要知识点:

(1)抽象类Decorator作为抽象类Component的子类,可以不重写父类的抽象方法,而是延迟到具体类ConcreteDecoratorA或ConcreteDecoratorB。

(2)Decorator继承Component,同时又聚合了Component,这使得使用具体装饰器装饰具体组件成为可能。

(3)装饰者和被装饰者可以独立变化。用户可以根据需要增加新的装饰类,在使用时再对其进行组合,原有代码无须改变,符合开闭原则。

(4)装饰者模式可以在运行时动态扩展一个对象的功能,比继承(编译时期就已经确定关系)更加灵活。

img

应用示例一:咖啡店收银程序设计

类图

IDEA2023.1绘制(突出了主要关系:泛化和关联,构造方法单独列出)

img
代码
/*
 * 装饰者模式示例:咖啡店收银程序。
 * 通过【继承+聚合】,实现给对象动态地增加功能
 * 抽象类Decorator作为抽象类Order的子类,并未重写基类的抽象方法,而是延迟到Decorator的子类里。
 */

abstract class Order{  //抽象订单类
   private String des; //订单描述

   public abstract float  cost(); //抽象方法:计算费用


   public String getDes() {
      return des;
   }
   public void setDes(String des) {
      this.des = des;
   }
}

class Coffee extends Order{   //表示被装饰对象
   public Coffee() {   //构造方法
      setDes("咖啡");
   }

   @Override
   public float cost() {  //实现抽象方法
      return 5.0f;
   }
}

abstract class Decorator extends Order{  //抽象装饰类
   private float price; //单价
   private Order order;  //聚合

   public Decorator(float price, Order order) {
      this.price = price;
      this.order = order;
   }

   public float getPrice() {
      return price;
   }

   public Order getOrder() {
      return order;
   }

   @Override
   public String getDes() {   //重写
      return super.getDes()+"  "+price+" && "+order.getDes();
   }
}


class Milk extends Decorator{  //具体装饰类
   public Milk(Order order) {
      super(2.0f,order);
      super.setDes("牛奶");  //此方法必须位于构造方法之后
   }

   @Override
   public float cost(){
      return super.getPrice()+super.getOrder().cost();
   }
}

class Chocolate extends Decorator{   //具体装饰类
   public Chocolate(Order order) {
      super(1.5f, order);
      super.setDes("巧克力");
   }

   @Override
   public float cost() {
      //具体装饰器的价格+先前订单的费用,体现动态扩展对象的功能
      return super.getPrice()+super.getOrder().cost();
   }
}

public class Client{
   public static void main(String[] args) {
      
      //step1
      Order order = new Coffee();  //初始订单,被装饰对象
      System.out.println("描述:"+order.getDes());
      System.out.println("费用:"+order.cost());
      System.out.println("=========");
      
      //step2
      order = new Milk(order);  //订单迭加,装饰
      System.out.println("order加一份牛奶后的费用:"+order.cost());
      System.out.println("描述="+order.getDes());
      System.out.println("============================");
      
      //step3
      order = new Chocolate(order);  //订单迭加,继续装饰
      System.out.println("order再加一份巧克力后的费用:"+order.cost());
      System.out.println("描述="+order.getDes());
      System.out.println("===============================================");
      
      //step4
      order = new Chocolate(order);  //订单迭加,继续装饰
      System.out.println("order再加一份巧克力后的费用:"+order.cost());
      System.out.println("描述="+order.getDes());
   }
}

应用示例二:制作冰雪皇后Dairy Queen

类图

img

**img

**

**要点:**装饰基类没有使用构造器注入基类对象,而是使用setter注入。

img
代码
undefinedpublic class Client{  //客户端
    public static void main(String[] args){
        //1个被装饰对象;无参
        DQ iceCream=new IceCream();
        //3个装饰对象;无参
        Decorator cookies=new Cookies();
        Decorator blueBerry=new BlueBerry();
        Decorator chocolateChip=new ChocolateChip();
        //依次进行3个装饰动作,形成对象嵌套结构
        cookies.setDq(iceCream);
        blueBerry.setDq(cookies);
        chocolateChip.setDq(blueBerry);
        //调用抽象方法,面对抽象编程
        chocolateChip.operation();
    }
}

abstract class DQ{  //角色1:抽象构件,也可设计为接口。
    protected abstract void operation(); //抽象方法
}

class IceCream extends DQ{  //角色2:具体构件(被装饰类)
    @Override
    public void operation(){
        System.out.print("DQ牌冰淇淋");
    }
}

abstract class Decorator extends DQ{  //角色3:抽象装饰类(核心类)
    private DQ dq; //聚合

    public void setDq(DQ dq){
        this.dq=dq;
    }

    @Override
    public void operation(){
        dq.operation();  //递归调用
    }
}

class Cookies extends Decorator{  //角色4:具体装饰类
    @Override
    public void operation(){  //抽象方法在具体装饰类里被重写
        super.operation();   //调用基类方法
        System.out.print(",添加了饼干");  //装饰
    }
}

class BlueBerry extends Decorator{  //角色4:具体装饰类
    @Override
    public void operation(){  //抽象方法在具体装饰类里被重写
        super.operation();
        System.out.print(",添加了蓝莓");
    }
}

class ChocolateChip extends Decorator{  //角色4:具体装饰类
    @Override
    public void operation(){  //抽象方法在具体装饰类里被重写
        super.operation();
        System.out.println(",添加了巧克力。");
    }
}

组合模式(Composite partten)

模式动机

树形结构在软件开发中随处可见,如操作系统的文件目录结构、应用软件中的菜单结构和公司组织机构等。

模式定义

组合模式(Composite Pattern)定义:组合多个对象形成树形结构以表示具有部分—整体关系的层次结构。组合模式让客户端可以统一地对待单个对象和组合对象。

组合模式又称部分—整体(Part—Whole)模式,它将对象组织到树形结构中,用以描述整体与部分的关系。

组合模式是对象结构型模式

模式结构与角色分析

img

组合模式根据抽象构件的定义形式,可划分为透明组合模式和安全组合模式。

【要点】

组合模式用于处理树型结构,包含Component(抽象构件)、Leaf(叶子构件)和Composite(容器构件)三个角色。

组合模式描述了如何将容器对象和叶子对象进行递归组合。

通过抽象机制,可以一致地对待容器对象和叶子对象。

透明组合模式

类图
img
代码
undefinedimport java.util.ArrayList;
import java.util.List;
/*
 * 组合模式用于处理树形结构,包含Component(抽象构件)、Leaf(叶子构件)和Composite(容器构件)三个角色。
 * 组合模式描述了如何将容器对象和叶子对象进行递归组合。
 * 通过抽象机制,可以一致地对待容器对象和叶子对象。
 */

abstract class Component{  //角色1:抽象构件

    protected  String name;  //组件名称

    public Component(String name) {  //构造方法
        this.name = name;
    }

    protected  abstract void operation();  //业务方法
    protected abstract void add(Component c);  //增加成员
    protected abstract void remove(Component c);    //删除成员
    protected abstract List<Component> getChild();  //获取成员
}

class Leaf extends Component{   //角色2:叶子构件

    public Leaf(String name) {
        super(name);  //调用父类构造方法
    }

    @Override
    protected void add(Component c) {
        throw new UnsupportedOperationException(); //可以是空实现
    }
    @Override
    protected void remove(Component c) {
        throw new UnsupportedOperationException(); //可以是空实现
    }
    @Override
    protected List<Component> getChild() {
        throw new UnsupportedOperationException(); //return null;
    }

    @Override
    protected void operation() {  //打印组件名称
        System.out.println(name);
    }

}

class Composite extends Component{  //角色3:容器构件

    //JDK 8泛型新特性——后面的泛型类可省略
    private List<Component> components=new ArrayList<>();  //Composite组合Component

    public Composite(String name) { //构造器
        super(name);
    }

    @Override
    public void add(Component c) {  
        components.add(c);
    }
    @Override
    protected void remove(Component c) {
        components.remove(c);
    }
    @Override
    protected List<Component> getChild() {   //返回子组件
        return components;
    }
    @Override
    protected void operation() {
        System.out.println("----"+name+"----");
        for(Component c:components) {  //遍历子组件
            c.operation();  //方法递归调用
        }
    }
}

public class Client {  //客户端
    public static void main(String[] args) {
        Component university,computerCollege,infoEngineerCollege;  //Client关联Component
        //创建根容器组件
        university = new Composite("清华大学");
        //创建2个子容器组件
        computerCollege = new Composite("计算机学院");
        university.add(computerCollege);
        infoEngineerCollege = new Composite("信息工程学院");
        university.add(infoEngineerCollege);
        //创建叶子组件并添加到相应的容器组件
        computerCollege.add(new Leaf("软件工程系"));
        computerCollege.add(new Leaf("网络工程系"));
        computerCollege.add(new Leaf("计算机科学与技术系"));
        infoEngineerCollege.add(new Leaf("通信工程系"));
        infoEngineerCollege.add(new Leaf("电子工程系"));

        university.operation(); //调用递归方法,遍历根容器组件
        //computerCollege.operation(); //调用递归方法,遍历特定子组件
        //new Leaf("计算机科学与技术系(专业)").operation();; //调用递归方法,遍历叶子组件
    }
}

安全组合模式

类图
img
代码

在使用透明组合模式代码的基础上,使用安全组合模式改写。

undefinedabstract class Component{  //角色1:抽象构件,表示抽象的根结果

    protected  String name;  //组件名称

    public Component(String name) {  //构造方法
        this.name = name;
    }

    protected  abstract void operation();  //业务方法
    /*protected abstract void add(Component c);  //增加成员
    protected abstract void remove(Component c);    //删除成员
    protected abstract List<Component> getChild();  //获取成员*/
}

class Leaf extends Component{   //角色2:叶子构件

    public Leaf(String name) {
        super(name);  //调用父类构造方法
    }

    /*@Override
    protected void add(Component c) {
        throw new UnsupportedOperationException(); //可以是空实现
    }
    @Override
    protected void remove(Component c) {
        throw new UnsupportedOperationException(); //可以是空实现
    }
    @Override
    protected List<Component> getChild() {
        throw new UnsupportedOperationException(); //return null;
    }*/

    @Override
    protected void operation() {  //打印组件名称
        System.out.println(name);
    }
}

class Composite extends Component{  //角色3:容器构件

    private List<Component> components=new ArrayList<>();  //Composite组合Component

    public Composite(String name) { //构造器
        super(name);
    }

    //@Override
    public void add(Component c) {  
        components.add(c);
    }
    //@Override
    protected void remove(Component c) {
        components.remove(c);
    }
    //@Override
    protected List<Component> getChild() {   //返回子组件
        return components;
    }

    @Override
    protected void operation() {
        System.out.println("----"+name+"----");
        for(Component c:components) {  //遍历子组件
            c.operation();  //方法递归调用
        }
    }
}

public class Client {  //客户端
    public static void main(String[] args) {
        //Component university,computerCollege,infoEngineerCollege;  //Client关联Component
        Composite university,computerCollege,infoEngineerCollege;  //Client关联Component
        //创建根容器组件
        university = new Composite("清华大学");
        //创建2个子容器组件
        computerCollege = new Composite("计算机学院");
        university.add(computerCollege);
        infoEngineerCollege = new Composite("信息工程学院");
        university.add(infoEngineerCollege);
        //创建叶子组件并添加到相应的容器组件
        computerCollege.add(new Leaf("软件工程系"));
        computerCollege.add(new Leaf("网络工程系"));
        computerCollege.add(new Leaf("计算机科学与技术系"));
        infoEngineerCollege.add(new Leaf("通信工程系"));
        infoEngineerCollege.add(new Leaf("电子工程系"));

        university.operation(); //调用递归方法,遍历根容器组件
        //computerCollege.operation(); //调用递归方法,遍历特定子组件
        //new Leaf("计算机科学与技术系(专业)").operation();; //调用递归方法,遍历叶子组件
    }
}

备忘录模式 (Memento Partten)

模式动机

场景:

  • 录入大批人员资料。正在录入当前人资料时,发现上一个人录错了,此时需要恢复上一个人的资料,再进行修改。
  • Word文档编辑时,忽然电脑死机或断电,再打开时,可以看到word提示你恢复到以前的文档
  • 管理系统中,公文撤回功能。公文发送出去后,想撤回来。

核心:

  • 就是保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态。

在应用软件的开发过程中,有时需要记录一个对象的内部状态。为了允许用户取消不确定的操作或从错误中恢复过来,需要实现备份点和撤销机制。为此,必须事先将状态信息保存在某处,然后将对象恢复到它们原先的状态。

对象通常封装了其部分或所有的状态信息,使得其状态不能被其它对象访问,也就不可能在该对象之外保存其状态,而暴露其内部状态又将违反封装的原则,可能有损系统的可靠性和可扩展性。

模式定义

在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

备忘录模式是一种对象行为型模式,其别名为Token(标记)模式。

模式结构与角色分析

IDEA类图:

img

角色1:原发器Originator,具有内部状态属性state和创建备忘录方法createMemento()和设置备忘录方法setMemento()。

角色2:备忘录Memento,包含内部状态属性state及其setter/getter方法,相当于一个实体类对象,用来存储Originator的内部状态。

角色3:负责人Caretaker,聚合Memento,负责保存备忘录,不能对备忘录的内容进行操作或检查。

要点:因为Memento存储的是Originator的中间状态,因此,只允许生成本备忘录的那个原发器修改备忘录的内部状态。

一次Undo(撤销)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:将原发器的方法setMemento()命名为setStateFromMemento()比较合适,也可使用重载方法setState(Memento)

**多次Undo**(撤销)与Redo(恢复):在类Caretaker里,使用List字段代替原来的Memmento。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

模式扩展

创建抽象原发器(包含备忘录和恢复备忘录2个抽象方法)及抽象备忘录(包含1个获取状态的抽象方法)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

模式评价

  • 备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤。当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
  • 备忘录模式保存了封装的边界信息,Memento对象是原发器对象的表示,不会被其它代码改动。这种模式简化了原发器对象,Memento只保存原发器的状态,采用堆栈来存储备忘录对象可以实现多次取消操作。
  • 备忘录模式的最大缺点就是资源消耗过大,如果类的成员变量太多,就不可避免占用大量的内存了,而且每保存一次对象的状态都需要消耗内存资源,如果知道这一点大家就容易理解为什么一些提供了Undo功能的软件在运行时需要的内存和硬盘空间比较大了。

使用情形

在以下情况下,可以使用备忘录模式。

  • 必须保存一个对象在某一个时刻的状态/部分状态,这样以后需要时它能够恢复到先前的状态。
  • 如果一个用接口来让其它对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

代码

package sy6_Behavioral_Pattern.memento;
/**
 * 备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,
 *          这样就可以在以后将对象恢复到原先保存的状态。
 * Originator:原发器,产生内部状态且需要保存的对象。
 * Memento:备忘录用于存储原发器的内部状态。
 * Caretaker:管理者,负责保存备忘录。
 *
 * 要点:Originator与Memento包含相同的状态属性, Caretaker通常会聚合Memento对象。
 * 说明:本例只能实现一次Undo
 */

public class Client {  //客户端
	public static void main(String[] args) {
		Originator originator = new Originator();  //创建一个游戏角色
		Caretaker caretaker = new Caretaker();    //创建游戏角色状态看管者

		originator.setState("状态#1 攻击力 100");
		System.out.println("当前状态:"+originator.getState());
		caretaker.setMemento(originator.createMemento());  //看管者设置备忘录为原发器创建的备忘录
		originator.setState("状态#2 攻击力 110");
		System.out.println("当前状态:"+originator.getState());
		caretaker.setMemento(originator.createMemento());  //看管者设置备忘录为原发器创建的备忘录
		originator.setState("状态#3 攻击力 80");
		System.out.println("当前状态:"+originator.getState());

		//撤销:只能 Undo到上一状态
		System.out.println("==撤销(Undo)到上一状态==");
		//将看管者的备忘录设置为原发器的备忘录(实现撤销操作)
		originator.setState(caretaker.getMemento());
		System.out.println("当前状态:"+originator.getState());
	}
}

class Memento{  //备忘录,相当于一个实体类
	private String state;

	public Memento(String state) {  //构造方法
		this.state = state;
	}
	public String getState() {
		return state;
	}
}

class Originator{  //角色:原发器Originator,提供了创建备忘录对象保存当前状态和恢复备忘录的方法
	private String state;

	public String getState() {
		return state;
	}
	public void setState(String state) {  //setter
		this.state = state;
	}
	public Memento createMemento() {  //保存当前状态至备忘录对象
		return new Memento(state);  //Originator与Memento是创建依赖
	}

	public void setState(Memento memento) {  //方法重载(参数不同)
		state=memento.getState();  //通过备忘录对象恢复到历史的状态
	}
}

class Caretaker{  //看管者——管理备忘录对象(保存和获取,不能修改)
	private Memento memento;  //聚合关系

	public Memento getMemento() {  //getter
		return memento;
	}
	public void setMemento(Memento memento) {  //setter
		this.memento = memento;
	}
}

观察者模式(Observer Partten)

模式动机

  • 建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。其中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者。显然,这些观察者之间可以没有任何联系,并可以根据需要增加和删除观察者,使得系统更易于扩展。这就是观察者模式的模式动机。

模式定义

观察者模式定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

观察者模式是一种对象行为型模式

### 模式结构与角色分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

角色1:抽象类Observer,定义了抽象方法update(Subject subject)。

角色2:目标Subject,通常为抽象类,定义了抽象方法attach()、detach()和普通方法notifyToAll()。

注意:在原版类图的Subject里,定义了方法notify()。而在Java中,notify()是Object定义(使用final修饰)的终结方法。因此,重新命名notify()为notifyToAll()。

角色3:具体子类ConcreteObserver,作为子类,需要重写方法update(Subject subject)。同时,定义了属性observerState,表示观察到的状态。

角色4:具体目标ConcreteSubject,除了重写抽象方法外,还定义了表示目标状态的属性subjectState。

要点如下:

(1)Subject定义的抽象方法attach(Observer observer)和detach(Observer observer)用于维系若干Observer对象集合(List observers)。

(2)Subject还定义了普通方法notifyToAll(),它包含了对Observer对象的调用,即存在Subject到Observer的关联关系。

(3)ConcreteObserver除了需要重写基类的抽象方法,还定义了表示观察到目标状态的属性observerState。其中,重写基类的抽象方法时使用了ConcreteSubject对象,这表明ConcreteObserver与ConcreteSubject之间的关联。

(4)ConcreteSubject除了需要重写基类的抽象方法,还定义了表示自身状态的属性subjectState。

v模式应用

img

模式评价

优点

  • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
  • 观察者模式支持广播通信。
  • 观察者模式符合“开闭原则”的要求。

缺点

  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

适用情形

在以下情况下可以使用观察者模式:

一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

一个对象必须通知其他对象,而并不知道这些对象是谁。

需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

模式应用

观察者模式在软件开发中应用非常广泛,如某电子商务网站可以在执行发送操作后给用户多个发送商品打折信息,某团队战斗游戏中某队友牺牲将给所有成员提示等等。凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。

使用观察者模式处理Java事件

  • JDK1.1版本及以后的各个版本中,事件处理模型采用基于观察者模式的委派事件模型(Delegation Event Model, DEM)。
  • 在DEM中,事件的发布者称为事件源(Event Source),而订阅者叫做事件监听器(Event Listener)。在这个过程中,可以通过事件对象(Event Object)来传递与事件相关的信息。
  • 事件源对象、事件监听对象(事件处理对象)和事件对象构成了Java事件处理模型三要素。

代码

package sy6_Behavioral_Pattern.observer;
/**
 * 观察者模式(Observer Pattern)研究观察目标和观察者两类对象之间的关系
 * 角色1:抽象观察者Observer
 * 角色2:观察目标Subject(被观察者)
 * 角色3:具体观察者ConcreateObserver
 * 角色4:具体目标ConcreateSubject
 * <p>
 * 要点如下:
 * (1)Subject定义了抽象方法attach(Observer observer)和detach(Observer observer),用以维系若干Observer对象集合。
 * (2)Subject还定义了普通方法notifyToAll(),它包含了对Observer对象的调用,即存在Subject到Observer的关联关系。
 * (3)ConcreateObserver除了需要重写基类的抽象方法,还定义了表示观察到目标状态的属性observerState。
 * 其中,重写基类的抽象方法时使用了ConcreateSubject对象,这表明ConcreateObserver与ConcreateSubject之间的关联。
 * (4)ConcreateSubject除了需要重写基类的抽象方法,还定义了表示自身状态的属性subjectState。
 * (5)设置观察目标状态的方法在具体主题里定义=>main()方法创建具体观察目标时,不能向上转型。
 */
import java.util.ArrayList;
import java.util.List;

public class Client{
    public static void main(String[] args){
        //创建一个观察目标
        Subject subject=new ConcreteSubject();
        //创建观察者
        Observer observer1=new ConcreteObserver("张三",(ConcreteSubject)subject);
        Observer observer2=new ConcreteObserver("李四",(ConcreteSubject)subject);
        Observer observer3=new ConcreteObserver("王五",(ConcreteSubject)subject);
        //添加观察者到观察目标;方法attach()和detach()的参数类型是Observer
        subject.attach(observer1);
        subject.attach(observer2);
        subject.attach(observer3);
        //观察目标设置状态;setSubjectState()方法由类 ConcreteSubject定义
        ((ConcreteSubject)subject).setSubjectState("明天下午考设计模式!");
        //观察目标发布通知
        subject.notifyToAll();

        System.out.println("==============================");
        subject.detach(observer3);  //删除一个观察者
        //观察目标更改状态;setSubjectState()方法由类 ConcreteSubject定义
        ((ConcreteSubject)subject).setSubjectState("后天上午考Java EE!");
        //观察目标发布通知
        subject.notifyToAll();
    }
}

abstract class Observer{  //抽象观察者
    public abstract void update(); //更新观察到的消息,无参
}

abstract class Subject{  //目标
    //维系若干观察者,体现Subject对Observer的关联
    private List<Observer> observerList=new ArrayList<>();

    public List<Observer> getObserverList(){
        return observerList;
    }

    public void notifyToAll(){ //发通知
        for(Observer observer: observerList){
            observer.update();
        }
    }

    public abstract void attach(Observer observer);   //附加

    public abstract void detach(Observer observer);  //移除
}

class ConcreteObserver extends Observer{   //具体观察者
    private String name;  //观察者名称
    private ConcreteSubject concreteSubject;  //体现对ConcreateSubject的关联

    public ConcreteObserver(String name,ConcreteSubject concreteSubject){  //有参构造方法
        this.name=name;
        this.concreteSubject=concreteSubject;
    }

    @Override
    public void update(){   //使用具体主题(被观察者)的状态
        String observerState=concreteSubject.getSubjectState();
        System.out.println(name+"收到通知:"+observerState);
    }
}

class ConcreteSubject extends Subject{   //具体目标

    private String subjectState;  //目标状态

    public String getSubjectState(){
        return subjectState;
    }

    public void setSubjectState(String subjectState){
        this.subjectState=subjectState;
    }

    @Override
    public void attach(Observer observer){
        getObserverList().add(observer);
    }

    @Override
    public void detach(Observer observer){
        getObserverList().remove(observer);
    }
}

状态模式State

模式动机

状态模式的基本使用

  • 考虑一个银行系统,一个账户对象的状态可能处于若干个不同状态之一:开户状态、正常状态、透支状态、冻结状态。**当顾客在对账户进行存取款操作时,账户类根据自身的当前状态作出不同的反应,同时进行对象状态的切换。**例如:如果账户处于冻结状态就没有办法再进行取款操作,一个取款操作需要先了解账户对象的状态。
  • 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象。这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。
  • 状态(State)模式描述了账户如何在每一种状态下表现出不同的行为。而一旦取款操作完成,对象的状态也将动态发生变化,如取款后账户余额低于某一值其状态可能从正常状态转为透支状态。
  • 状态模式的关键是引入了一个抽象类来专门表示对象的状态,这个类我们叫做状态类,而对象的具体状态都继承了该类,并在不同具体状态类中实现了不同状态的行为,包括各种状态之间的转换。

模式定义

状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

状态模式别名为状态对象(Objects for States),它是一种对象行为型模式

模式结构与角色分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

角色1:抽象状态State,定义了抽象方法handle()。

角色2:具体状态ConcreteState,继承自State,重写了State定义的抽象方法。

角色3:上下文Context聚合了State类型的对象state,主要定义了供客户端使用的方法request()。

【要点】Context聚合了State,方法request()里调用State的抽象抽象方法handle()。

【总结】在状态模式中,上下文类Context包含一个抽象状态类型的对象,并提供一些可以改变具体状态的方法。

通过使用状态模式,可以实现状态转换的封装,从而提高代码的可维护性和可扩展性。通过在不同的状态之间进行转换来改变其行为,而无需改变其本身的代码。

模式评价

  • 封装转换过程,也就是转换规则。
  • 枚举可能的状态,因此,需要事先确定状态种类。
  • 将所有与某个状态有关的行为放到一个对象里。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块或switch语句。
  • 避免了状态的不一致性,因为状态的改变只使用一个状态对象而不是几个对象或属性。
  • 状态模式的使用会增加系统类和对象的个数。

使用情形

  • 对象的行为依赖于它的状态(属性)。并且,它必须可以根据它的状态改变而改变它的相关行为。
  • 操作的很多部分都带有与对象状态有关的大量条件语句,大量条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加删除状态,使客户类与类库之间的耦合增强。

模式应用:模拟交通信号灯

类图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码
undefinedpublic class Client {  //客户端
    public static void main(String[] args) throws InterruptedException{
       //创建上下文对象
       Context context = new Context();
       //连续地通过上下文对象请求
       while(true){
          context.request();
          Thread.sleep(3000);
       }
    }
}

abstract class State {  //抽象状态
    public abstract void handle(Context context);
}

class ConcreteStateA extends State {  //具体状态
    @Override
    public void handle(Context context) {  //重写抽象方法
       System.out.println("ConcreteStateA---绿灯");
       context.setState(new ConcreteStateB());  //重设上下文状态
    }
}

class ConcreteStateB extends State {  //具体状态
    @Override
    public void handle(Context context) {  //重写抽象方法
       System.out.println("ConcreteStateB---黄灯");
       context.setState(new ConcreteStateC());
    }
}

class ConcreteStateC extends State {
    @Override
    public void handle(Context context) {
       System.out.println("ConcreteStateC---红灯");
       context.setState(new ConcreteStateA());
    }
}

class Context {  //上下文
    private State state;  //Context聚合State

    public Context() {   //构造方法
       state=new ConcreteStateA();  //设置初始状态
    }
    public void setState(State state) {  //setter注入
       this.state = state;
    }
    public void request() {  //供客户端请求的方法
       state.handle(this);
    }
}

模式应用:模拟电梯运行

类图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码
undefined/**
 * 考虑电梯的三种状态:open、close和 run,根据当前状态动态地改变其行为。
 */

public class Client {  //客户端代码
    public static void main(String[] args) {
        //1、创建上下文;初始状态为关闭
        Elevator elevator = new Elevator();
        //2、使用上下文
        elevator.open(); //输出:电梯打开
        elevator.close(); //输出:电梯已经关闭
        elevator.run(); //输出:电梯开始运行
        elevator.setState(new OpenState());  //强行设置open状态,即跳过close状态
        elevator.open(); //输出:电梯已经打开
        elevator.close(); //输出:电梯关闭
        elevator.run(); //输出:电梯不能运行
    }
}

interface ElevatorState {  //角色一:抽象状态接口
    void open();
    void close();
    void run();
}

class Elevator {   //角色二:上下文类
    private ElevatorState state;  //关联关系

    public Elevator() {   //构造方法
        state = new CloseState();   //设置初始状态为关闭
    }

    public void setState(ElevatorState state) {  //setter,设置新状态
        this.state = state;
    }

    //使用关联的对象实现电梯的三种动作(对应于抽象状态接口的三种方法)
    public void open() {
        state.open();
    }
    public void close() {
        state.close();
    }
    public void run() {
        state.run();
    }
}

class OpenState implements ElevatorState {  //角色三:具体状态类--打开状态下的行为
    public void open() {
        System.out.println("电梯已经打开");
    }
    public void close() {
        System.out.println("电梯关闭");
    }
    public void run() {
        System.out.println("电梯不能运行");
    }
}

class CloseState implements ElevatorState {  //具体状态类--关闭状态下的行为
    public void open() {
        System.out.println("电梯打开");
    }
    public void close() {
        System.out.println("电梯已经关闭");
    }
    public void run() {
        System.out.println("电梯开始运行");
    }
}

class RunState implements ElevatorState {  //具体状态类--运行状态下的行为
    public void open() {
        System.out.println("电梯不能打开");
    }
    public void close() {
        System.out.println("电梯已经关闭");
    }
    public void run() {
        System.out.println("电梯正在运行");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值