目录

  • (一) 模板方法模式
  • (二) 命令模式
  • (三) 访问者模式
  • (四) 迭代器模式
  • (五) 观察者模式
  • (六) 中介者模式
  • (七) 备忘录模式
  • (八) 解释器模式

(一) 模板方法模式

模板方法模式(Template Method Pattern): 又称之为模板模式, 在一个抽象类公开定义了执行它的方法的模板, 它的子类可以俺需要重写方法实现, 但调用将以抽象类中定义的方式进行. 属于行为型模式.

简单来说, 抽象类中的模板方法定义一个操作中的算法的骨架(如多个抽象方法的执行顺序), 而将一些步骤(抽象方法)延迟到子类中实现, 使得子类可以不改变一个算法的结构, 就可以定义该算法的某些特定的步骤.

举例: 玩电子游戏一般会经历三个步骤: 下载游戏 —> 开始游戏 —> 结束游戏. 可以采用模板模式思想来实现:

  • 玩游戏的三个步骤顺序是不可变(算法的骨架), 可将这个三个步骤定义一个抽象类的模板方法
  • 三个步骤中具体操作的是哪个游戏, 抽象类是无法预测的, 需要由具体的子类去实现

游戏抽象类: 定义三个步骤 和 玩游戏的模板方法

abstract class Game {// 下载游戏abstract void download();// 玩游戏中abstract void playing();// 游戏结束abstract void over();// final修饰 模板方法final void playGame() {download();playing();over();}
}

游戏实现类: 指定游戏的类型

class LOL extends Game {@Overridevoid download() {System.out.println("LOL 下载中...");}@Overridevoid playing() {System.out.println("LOL 游戏中...");}@Overridevoid over() {System.out.println("LOL 结束");}
}class DNF extends Game {@Overridevoid download() {System.out.println("DNF 下载中...");}@Overridevoid playing() {System.out.println("DNF 游戏中...");}@Overridevoid over() {System.out.println("DNF 结束");}
}

测试:

public class TemplatePattern {System.out.println("-------- LOL 游戏 --------");Game LOL = new LOL();LOL.playGame();System.out.println("-------- DNF 游戏 --------");Game DNF = new DNF();DNF.playGame();
}

模板方法模式的钩子方法: 在模板方法模式中的抽象类中, 定义一个方法, 默认不做任何事情, 子类可以视情况来覆盖重写此方法. 该方法称之为 钩子方法 (比如: 可以用这个方法来判断模板方法中是否要执行某些步骤)

还是用上面游戏的例子来举例: 现在游戏抽象类类中的模板方法中执行了三步骤: 下载游戏 —> 开始游戏 —> 结束游戏. 但是, 有些游戏是不需要下载的(如: 4399小游戏), 因此我们需要 钩子方法 来判断是否要执行 下载游戏的步骤

abstract class Game {abstract void download();abstract void playing();abstract void over();// 钩子方法(默认返回true): 来判断是否要执行 下载游戏的步骤boolean isDownload() {return true;}// final修饰 模板方法final void playGame() {if (isDownload()) {download();}playing();over();}
}class Game4339 extends Game {// 覆盖重写isDownload钩子方法, 返回false: 取消下载游戏的步骤@Overrideboolean isDownload() {return false;}// 4339无需下载@Overridevoid download() {System.out.println("4339小游戏无需下载");}@Overridevoid playing() {System.out.println("4339 游戏中...");}@Overridevoid over() {System.out.println("4339 结束");}
}public class TemplatePattern {public static void main(String[] args) {System.out.println("-------- LOL 游戏 --------");Game LOL = new LOL();LOL.playGame();System.out.println("-------- DNF 游戏 --------");Game DNF = new DNF();DNF.playGame();System.out.println("-------- 4399 游戏 --------");Game game4339 = new Game4339();game4339.playGame();}
}


模板方法模式的注意事项和细节

  • 算法只存在于抽象的父类中模板方法, 当修改算法时, 子类会自动继承这些修改
  • 实现了最大化代码复用, 即统一了算法, 也提供了很大的灵活性. 将算法中的步骤推迟到子类实现
  • 抽象类中模板方法 需要用 final 关键字修饰, 防止子类重写模板方法
  • 缺点: 每个不同的实现都需要一个子类实现, 导致类的个数增加, 使得系统更加庞大

(二) 命令模式

在软件系统中, 我们经常需要向某些对象发送请求访问, 然后执行方法. 此时, “行为请求者” 与 “行为实现者” 通常呈现一种 “紧耦合”

命令模式(Command Pattern): 将一个请求封装成一个命令对象, 以便使用不同的参数来表示不同的请求, 并组合 “行为实现者” 对象 来执行真正的方法

命令模式封装 命令对象 , 请求的发送者通过对应的命令对象调用请求的执行者, 让对象之间的调用关系更灵活, 实现 请求的发送者 与 请求的执行者 解耦

举例: 随着5G时代的到来, 诞生基于物联网的一套智能家具: 通过一个App来控制所有只能家具的开关, 如: 第一组开关控制电灯, 第二组开关控制电视, 第三组开关控制音响…


命令模式中的角色

  • Invoker调用者角色: 相当于智能家具的App界面, 通过命令对象来调用接受者角色, 如: 点击了第二组开关(电视)的开按钮
  • Receiver接受者角色: 真正执行的功能, 如: 电灯角色的开关功能, 电视角色的开关功能…
  • Command命令角色: 通常为接口或实现类, 提供命令执行的抽象方法(撤销的方法)
  • ConcreteCommand命令接口实现对象: 组合一个接受者对象, 并将具体的命令绑定到具体的操作中

Receiver接受者角色(真正执行的功能)

/*** 电灯接受者类: 提供开和关的两个方法*/
class Light {public void on() {System.out.println("打开电灯");}public void off() {System.out.println("关闭电灯");}
}/*** 电视接受者类: 提供开和关的两个方法*/
class TV {public void on() {System.out.println("启动电视");}public void off() {System.out.println("关闭电视");}
}

Command命令角色(通常为接口或实现类, 提供命令执行的抽象方法)

/*** 命令接口: 提供一个执行的抽象方法*/
interface Command {void execute();
}

ConcreteCommand命令接口实现对象(组合一个接受者对象, 并将具体的命令绑定到具体的操作中)

/*** 开灯命令类: 实现Command命令接口, 提供一个电灯类的成员属性(为开灯命令execute方法执行提供 具体的执行者)*/
class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}
}/*** 关灯命令类: 实现Command命令接口, 提供一个电灯类的成员属性(为关灯灯命令execute方法执行提供 具体的执行者)*/
class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}
}/*** 启动电视命令类: 实现Command命令接口, 提供一个电视类的成员属性(为启动电视命令execute方法执行提供 具体的执行者)*/
class TVOnCommand implements Command {private TV tv;public TVOnCommand(TV tv) {this.tv = tv;}@Overridepublic void execute() {tv.on();}
}/*** 关闭电视命令类: 实现Command命令接口, 提供一个电视类的成员属性(为关闭电视命令execute方法执行提供 具体的执行者)*/
class TVOffCommand implements Command {private TV tv;public TVOffCommand(TV tv) {this.tv = tv;}@Overridepublic void execute() {tv.off();}
}

Invoker调用者角色(智能家具App类)

class RemoteControlApp {private Command[] onCommand; // 开命令数组(开灯, 启动电视...)private Command[] offCommand; // 关命令数组(关灯, 关闭电视...)// 初始化命令数组的大小容量public RemoteControlApp(int commandSize) {this.onCommand = new Command[commandSize];this.offCommand = new Command[commandSize];}// 为每组开关设置对应的命令public void setCommand(int index, Command onCommand, Command offCommand) {this.onCommand[index] = onCommand;this.offCommand[index] = offCommand;}// 点击第index组的开的按钮public void on(int index) {this.onCommand[index].execute();}// 点击第index组的关的按钮public void off(int index) {this.offCommand[index].execute();}
}

测试:

public class CommandPattern {public static void main(String[] args) {// 创建电灯、电视实体类: 开和关功能的实际提供者Light light = new Light();TV tv = new TV();// 创建电灯、电视的开和关的命令类LightOnCommand lightOnCommand = new LightOnCommand(light);LightOffCommand lightOffCommand = new LightOffCommand(light);TVOnCommand tvOnCommand = new TVOnCommand(tv);TVOffCommand tvOffCommand = new TVOffCommand(tv);// 为每组开关设置对应的命令RemoteControlApp app = new RemoteControlApp(2);app.setCommand(0, lightOnCommand, lightOffCommand);app.setCommand(1, tvOnCommand, tvOffCommand);app.on(0);app.off(0);app.on(1);app.off(1);}
}


此时, 如果再需要提供一组音响的开关, 无需修改源代码. 只需要提供: 音响类(开和关功能的实际提供者), 然后添加到智能家具App类的对应命令数组中, 即可

命令模式的注意事项和细节

  • 发起请求的对象(调用者) 通过 命令对象 实现与执行请求的对象(接受者)解耦, 调用者只需要调用命令对象的excute()方法就可以是让接受者执行功能. 无需知道接受者对象的任何细节
  • 可以设计成一个命令队列, 把命令对象放到队列中, 就可以让多线程执行命令
  • 命令模式适用于: 触发-反馈机制、订单的撤销与恢复
  • 缺点: 导致这个应用中存在大量的具体命令类, 增加了系统的复杂度

(三) 访问者模式

访问者模式(Visitor Pattern): 是一种 数据结构数据操作 分离的设计模式, 解决 数据结构 和 数据操作 的耦合问题

  • 数据结构: 是一个存储元素(类)的容器集合
  • 数据操作: 对容器集合的元素(类)的操作

访问者模式: 将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式

访问模式的工作原理: 在 被访问的类 中对外提供一个 接待访问者的方法, 并在这个方法中把 被访问的类 传递给 访问者

举例: 在高中时期, 学生是分为两种类型: 理科学生、文科学生. 在一次年级考试成绩汇总中:

  • 理科年级主任希望知道: 对理科学生的 主课成绩 和 理科成绩, 而对文科学生只需要知道主课成绩, 以作对比
  • 文科年级主任正好相反: 对文科学生的 主课成绩 和 理科成绩, 而对理科学生只需要知道主课成绩, 以作对比

可以采用访问者模式思想来实现:

  • 访问者模式中的数据结构: 学生类集合
  • 访问者模式中的数据操作: 不同类型的年纪主任 对 学生成绩 的 不同汇总

访问者模式中角色:

  • Element: 抽象的被访问者, 通常为接口或抽象类, 定义被访问者的公共属性(学生的名称、主课成绩), 以及提供一个 接待(接受)访问者的抽象方法
  • ConcreteElement: 具体的被访问者(理科学生、文科学生), 实现或继承Element类. 定义自己的属性(理科学生的理科成绩、文科学生的文科成绩)
  • Visitor: 抽象的访问者, 通常为接口或者抽象类, 它为每一种ConcreteElement具体被访问者定义了访问的抽象方法, 因此访问者模式尽可能要求ConcreteElement个数稳定
  • ConcreteVisitor: 具体的访问者(文、理科年纪主任), 实现或继承Visitor类, 访问每一种ConcreteElement类, 并在访问时对数据(成绩属性)操作
  • ObjectStructure: 容器集合, 是一个包含元素角色(Element)的容器, 提供让访问者对象遍历容器中的所有元素的方法

Element(抽象的被访问者): 学生抽象类, 定义被访问者的公共属性

abstract class Student {private String name; // 学生名称private int Chinese; // 语文成绩private int English; // 英语成绩private int Math; // 数学成绩public Student(String name, int Chinese, int English, int Math) {this.name = name;this.Chinese = Chinese;this.English = English;this.Math = Math;}// 在 被访问的类 中对外提供一个 接待访问者的抽象方法public abstract void accept(Visitor visitor);
}

ConcreteElement(具体的被访问者): 理科学生、文科学生

class LiKeStudent extends Student {private int WuLi; // 物理成绩private int ShengWu; // 生物成绩private int HuaXue; // 化学成绩public LiKeStudent(String name, int Chinese, int English, int Math, int wuLi, int shengWu, int huaXue) {super(name, Chinese, English, Math);WuLi = wuLi;ShengWu = shengWu;HuaXue = huaXue;}// 重写接待访问者的方法, 并将自身对象this传递给访问者, 以便访问者统计理科学生的成绩@Overridepublic void accept(Visitor visitor) {visitor.total(this);}
}class WenKeStudent extends Student {// 文科成绩private int DiLi;private int ZhengZhi;private int LiShi;public WenKeStudent(String name, int Chinese, int English, int Math, int diLi, int zhengZhi, int liShi) {super(name, Chinese, English, Math);DiLi = diLi;ZhengZhi = zhengZhi;LiShi = liShi;}// 重写接待访问者的方法, 并将自身对象this传递给访问者, 以便访问者统计文科学生的成绩@Overridepublic void accept(Visitor visitor) {visitor.total(this);}
}

Visitor(抽象的访问者): 它为每一种具体被访问者(文科、理科学生对象)定义了访问的抽象方法(统计成绩的方法)

interface Visitor {// 统计理科学生的抽象方法void total(LiKeStudent student);// 统计文科学生的抽象方法void total(WenKeStudent student);
}

理科年级主任具体访问者

class liKeVisitor implements Visitor {// 对理科学生统计 主课成绩 和 理科成绩@Overridepublic void total(LiKeStudent student) {System.out.print(student.getName() + "的主课成绩 = " + (student.getChinese() + student.getMath() + student.getEnglish()));System.out.println(", 理科成绩 = " + (student.getWuLi() + student.getHuaXue() + student.getShengWu()));}// 对文科学生只统计 主课成绩@Overridepublic void total(WenKeStudent student) {System.out.println(student.getName() + "的主课成绩 = " + (student.getChinese() + student.getMath() + student.getEnglish()));}
}

文科年级主任具体访问者

class WenKeVisitor implements Visitor {// 对文科学生统计 主课成绩 和 文科成绩@Overridepublic void total(LiKeStudent student) {System.out.println(student.getName() + "的主课成绩 = " + (student.getChinese() + student.getMath() + student.getEnglish()));}// 对理科学生只统计 主课成绩@Overridepublic void total(WenKeStudent student) {System.out.print(student.getName() + "的主课成绩 = " + (student.getChinese() + student.getMath() + student.getEnglish()));System.out.println(", 文科成绩 = " + (student.getDiLi() + student.getLiShi() + student.getZhengZhi()));}
}

ObjectStructure(对象结构角色): 维护着一个存储Student的集合

class ObjectStructure {// 提供存储Student元素的集合private List<Student> students = new ArrayList<>();// 添加Student元素public void attach(Student student) {students.add(student);}// 根据不同的访问者, 获取学生成绩public void totalCount(Visitor visitor) {for (Student student : students) {student.accept(visitor);}}
}

测试:

public class VisitorPattern2 {public static void main(String[] args) {LiKeStudent zhangSan = new LiKeStudent("理科学生: 张三", 80, 80, 80, 50, 50, 50);WenKeStudent liSi = new WenKeStudent("文科学生: 李四", 90, 90, 90, 60, 60, 60);ObjectStructure objectStructure = new ObjectStructure();objectStructure.attach(zhangSan);objectStructure.attach(liSi);System.out.println("=============理科年纪主任获取学生成绩==============");objectStructure.totalCount(new liKeVisitor());System.out.println();System.out.println("=============文科年纪主任获取学生成绩==============");objectStructure.totalCount(new WenKeVisitor());}
}


通过使用访问者模式实现了需求, 但并没有感觉到访问者模式的好处. 此时, 再来一个访问者(校长), 他需要看所有学生的汇总成绩(不分文理科), 我们只需添加一个访问者即可, 无需关心学生区分是文科生还是理科生, 因为对Visitor对不同类型的学生分开处理了

class XiaoZhangVisitor implements Visitor {@Overridepublic void total(LiKeStudent student) {System.out.println(student.getName() + "的总成绩 = " + (student.getChinese() + student.getMath() + student.getEnglish() + student.getChinese() + student.getMath() + student.getEnglish()));}@Overridepublic void total(WenKeStudent student) {System.out.println(student.getName() + "的总成绩 = " + (student.getChinese() + student.getMath() + student.getEnglish() + student.getDiLi() + student.getLiShi() + student.getZhengZhi()));}
}public class VisitorPattern2 {public static void main(String[] args) {LiKeStudent zhangSan = new LiKeStudent("理科学生: 张三", 80, 80, 80, 50, 50, 50);WenKeStudent liSi = new WenKeStudent("文科学生: 李四", 90, 90, 90, 60, 60, 60);ObjectStructure objectStructure = new ObjectStructure();objectStructure.attach(zhangSan);objectStructure.attach(liSi);System.out.println("=============理科年纪主任获取学生成绩==============");objectStructure.totalCount(new liKeVisitor());System.out.println();System.out.println("=============文科年纪主任获取学生成绩==============");objectStructure.totalCount(new WenKeVisitor());System.out.println();System.out.println("=============校长获取学生成绩==============");objectStructure.totalCount(new XiaoZhangVisitor());}
}


访问者模式的注意事项和细节

  • 访问者模式能在不修改对象容器结构中的元素的情况下, 为容器中的元素添加新的功能(访问者)
  • 访问者符合单一职责原则, 访问者把对元素的某种操作行为封装在一起, 使得访问者的功能单一
  • 破坏类的封装性: 具体的元素对访问者公布细节, 在accept()方法中, 把本类对象传递给访问者
  • 违背开闭原则: 对新增元素类型, 需要在访问者中添加对应的新增元素的具体访问操作方法, .
  • 违背依赖导致原则: 访问者依赖的是具体元素, 而不是抽象类 total(LiKeStudent)、total(WenKeStudent)
  • 适用于一个系统中有比较稳定的数据结构(具体元素类型稳定不变), 但有经常变化的功能需求(对具体元素的操作需求)的场景

(四) 迭代器模式

迭代器模式(Iterator Pattern): 提供一种遍历容器中元素的同一接口, 用一致的方法遍历容器中的元素, 不需要知道容器对象的底层实现(数组, 集合…), 即: 不暴露其内部的结构

迭代器模式中的角色:

  • Iterator: 迭代器接口, 一般包含两个方法:

    • boolean hasNext(): 返回布尔值, 判断是否有下一个元素
    • Object next(): 获取当前元素
  • ConcreteIterator: 具体迭代器, 组合要遍历的对象容器, 根据对象容器的结构类型来实现 hasNext() 和 next() 方法
  • Aggregate: 抽象聚合接口, 提供一个返回 Iterator 对象的 crateIterator() 抽象方法
  • ConcreteAggregate: 具体聚合类, 组合存储对象的容器, 根据对象容器的结构类型来实现 crateIterator() 方法, 返回具体迭代器

举例: 一个班级有多个学生, 当不同的程序员来实现这个需求时, 可能选择的储存学生对象的容器是不同的: 可以是Array数组, 也有可能是List集合…

学生实体, 容器存储的元素对象

class Student {String name;public Student(String name) {this.name = name;}public String getName() {return name;}
}

Iterator(迭代器接口)

interface Iterator {boolean hasNext(); // 返回布尔值, 判断是否有下一个元素Object next(); // 获取当前元素
}

ConcreteIterator(具体迭代器): 存储结构为 数组 和 List集合 的迭代器, 分别实现hasNext() 和 next() 方法

class ArrayStudentIterator implements Iterator {private Student[] students;private int currentIndex; // 当前遍历索引public ArrayStudentIterator(Student[] students) {this.students = students;this.currentIndex = 0;}@Overridepublic boolean hasNext() {if (currentIndex > students.length - 1 || students[currentIndex] == null) {return false;}return true;}@Overridepublic Object next() {return students[currentIndex++];}
}// 存储结构为List集合的迭代器
class ListStudentIterator implements Iterator {private List<Student> students;private int currentIndex; // 当前遍历索引public ListStudentIterator(List<Student> students) {this.students = students;this.currentIndex = -1;}@Overridepublic boolean hasNext() {if (currentIndex > students.size() - 2) {return false;}return true;}@Overridepublic Object next() {return students.get(++currentIndex);}
}

Aggregate(抽象聚合接口): 提供一个返回 Iterator 对象的 crateIterator() 抽象方法

interface Aggregate {Iterator createIterator();
}

ConcreteAggregate(具体聚合类): 根据存储的容器类型不同, 返回不同的Iterator迭代器

class ArrayStudent implements Aggregate {private Student[] students;// 初始化数据public ArrayStudent() {students = new Student[5];for (int i = 0; i < 5; i++) {students[i] = new Student("学生" + (i + 1));}}@Overridepublic Iterator createIterator() {return new ArrayStudentIterator(students);}
}class ListStudent implements Aggregate {private List<Student> students;// 初始化数据public ListStudent() {students = new ArrayList<>();for (int i = 5; i < 10; i++) {students.add(new Student("学生" + (i + 1)));}}@Overridepublic Iterator createIterator() {return new ListStudentIterator(students);}
}

测试:

public class IteratorPattern {public static void main(String[] args) {System.out.println("===========数组Iterator===============");ArrayStudent arrayStudent = new ArrayStudent();Iterator arrayIterator = arrayStudent.createIterator();while (arrayIterator.hasNext()) {System.out.println(((Student) arrayIterator.next()).getName());}System.out.println("===========集合Iterator===============");ListStudent listStudent = new ListStudent();Iterator ListIterator = listStudent.createIterator();while (ListIterator.hasNext()) {System.out.println(((Student) ListIterator.next()).getName());}}
}


迭代器模式的注意事项和细节

  • 提供一个统一的方法遍历对象, 隐藏了聚合的内部结构, 客户端只能通过迭代器遍历聚合元素, 而不知道聚合的具体组成
  • 每个聚合对象都要创建一个迭代器, 会生成大量的迭代器类.

(五) 观察者模式

观察者模式(Observer Pattern): 又称之为发布-订阅模式, 定义对象之间的一对多的依赖关系, 一的一方是被依赖的对象, 多的一方是依赖对象. 当被依赖对象的状态发送改变时, 需要通知依赖它的所有对象并自动更新

观察者模式中的角色:

  • Subject: 抽象主题接口, 定义注册(register)和删除(remove)观察者的抽象方法, 以及通知(notify)所有观察者的抽象方法
  • ConcreteSubject: 具体主题类, 被依赖的类(一的一方) . 内部维护着一个观察者对象集合
  • Observer: 抽象观察者接口, 定义一个更新(update)的抽象方法, 以供notify方法调用
  • ConcreteObserver: 具体观察者类, 实现Observer接口, 重写update方法

举例: 各大互联网公司的首页网站都会去访问气象局的提供的天气数据接口, 一旦气象局获取到新的天气数据, 要立即通知所有的使用它接口的网站更新到最新的天气数据

Subject主题接口 和 Observer观察者接口

interface Subject {void registerObserver(Observer observer); // 注册观察者void removeObserver(Observer observer); // 移除观察者void notifyObserver(); // 通知所有的已注册的观察者
}interface Observer {void update(String weatherInfo); // 观察者更新天气的抽象方法
}

ConcreteSubject(气象局主题类)

class WeatherBureau implements Subject {// 天气信息private String weatherInfo;// 维护着已注册的观察者private List<Observer> observers = new ArrayList<>();// 设置最新的天气信息, 通知所有的已注册的观察者public void setNewWeather(String weatherInfo) {this.weatherInfo = weatherInfo;notifyObserver();}// 注册观察者@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}// 移除观察者@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}// 通知所有的已注册的观察者@Overridepublic void notifyObserver() {for (Observer o : observers) {o.update(this.weatherInfo);}}
}

ConcreteObserver: 百度、腾讯具体的观察者

class BaiduObserver implements Observer {@Overridepublic void update(String weatherInfo) {System.out.println("腾讯官网" + weatherInfo);}
}class TencentObserver implements Observer {@Overridepublic void update(String weatherInfo) {System.out.println("百度官网" + weatherInfo);}
}

测试:

public class ObserverPattern {public static void main(String[] args) {// 气象局主题WeatherBureau weatherBureau = new WeatherBureau();// 注册百度观察者weatherBureau.registerObserver(new BaiduObserver());// 注册腾讯观察者weatherBureau.registerObserver(new TencentObserver());// 气象局获取到最新天气信息, 方法内部会通知所有观察者更新weatherBureau.setNewWeather("最新天气消息: 晴转多云");}
}

(六) 中介者模式

中介者模式(Mediator Pattern): 提供一个中介者对象来封装一系列的对象交互, 或者说中介者对象处理不同类之间的通信(相互调用), 使得各个对象不需要显示地相互调用, 从而使得其耦合松散, 而且可以独立地改变他们之间的交互

中介者模式中的角色:

  • Mediator: 抽象中介者: 将原先各个类之间的相互依赖并调用对方的方法, 转换为 各个类只依赖中介者对象, 将调用的方法抽取到中介者对象中, 由具体的中介者对象实现 (各个类在中介者模式中称之为 同事类)
  • CollcreteMediator: 具体的中介者, 内部会维护所有的同事类(Map集合), 同时实现共有方法, 根据不同的同事类实现不同的逻辑
  • Colleague: 抽象的同事类, 组合依赖中介者对象, 通过中介者对象来实现共同方法
  • CollcreteMediator: 具体同事类, 无需知道(依赖调用)其他数据库同事类, 通过中介者对象来调用实现功能

举例: 为了保证线上系统的数据安全和快速读取, 一般会提供三个数据库:

  • 主数据库: 当主数据库数据更新时, 需要同步到 备份数据库 和 Redis数据库
  • Redis数据库: 快速读取, 当Redis数据库数据更新时, 需要同步到 备份数据库 和 主数据库
  • 备份数据库: 数据备份, 保证数据安全. 无法手动操作备份数据库

未采用中介者模式来实现此需求, 代码如下:

class MainMysql {private List<String> dataList = new ArrayList<>(); // 用集合表示数据库存储内容private Redis redis; // 组合Redis对象private BackupMysql backupMysql; // 组合BackupMysql 对象public void addData(String data) {dataList.add(data);System.out.println(data + " 添加到主数据库中, 主数据库存储元素: " + dataList);SyncData(data);}// 同步方法: 将主数据库的更新数据 同步到 Redis数据库 和 备份数据库public void SyncData(String data) {redis.getDataList().add(data);System.out.println(data + " 同步到Redis数据库中, Redis数据库存储元素: " + dataList);backupMysql.getDataList().add(data);System.out.println(data + " 同步到备份数据库中, 备份数据库存储元素: " + dataList);}
}class Redis {private List<String> dataList = new ArrayList<>(); // 用集合表示数据库存储内容private MainMysql mainMysql; // 组合MainMysql 对象private BackupMysql backupMysql; // 组合BackupMysql 对象public void addData(String data) {dataList.add(data);System.out.println(data + " 添加到Redis数据库中, Redis数据库存储元素: " + dataList);SyncData(data);}// 同步方法: 将Redis数据库的更新数据 同步到 主数据库 和 备份数据库public void SyncData(String data) {mainMysql.getDataList().add(data);System.out.println(data + " 同步到主数据库中, 主数据库存储元素: " + dataList);backupMysql.getDataList().add(data);System.out.println(data + " 同步到备份数据库中, 备份数据库存储元素: " + dataList);}
}class BackupMysql {private List<String> dataList = new ArrayList<>(); // 用集合表示数据库存储内容
}

测试:

public class MediatorPattern_Error {public static void main(String[] args) {MainMysql mainMysql = new MainMysql();Redis redis = new Redis();BackupMysql backupMysql = new BackupMysql();mainMysql.setBackupMysql(backupMysql);mainMysql.setRedis(redis);redis.setMainMysql(mainMysql);redis.setBackupMysql(backupMysql);mainMysql.addData("张三");System.out.println();redis.addData("李四");}
}


上述代码已实现了需求, 但出现 数据库类复杂且相互引用, 扩展性、维护性难度大 等问题

接下来使用中介者模式来实现需求: 将 “调用数据同步方法” 的操作交给中介者类, 所有数据库类只依赖中介者对象, 与其他数据库类无关联

Mediator(抽象中介者): 将 “调用数据同步方法” 的操作交给中介者类, 并提供一个注册数据库同事对象的方法(将数据库同事对象添加到其子类的集合中)

abstract class Mediator {// 注册数据库对象, 将数据库同事对象添加到中介者维护的数据库对象集合public abstract void register(String databaseName, DatabaseColleague databaseColleague);// 同步数据的抽象方法public abstract void SyncData(String databaseName, String data);
}

ConcreteMediator(具体的中介者): 内部维护着所有数据库同事对象Map集合(通过数据库名称来区分数据库) 和 具体实现数据同步方法(通过不同的数据库名称, 来实现不同数据库的同步数据操作)

class ConcreteMediator extends Mediator {// 维护着所有数据库同事对象private Map<String, DatabaseColleague> databaseMap;public ConcreteMediator() {databaseMap = new HashMap<>();}// 将数据库同事对象添加到Map集合中, 通过 databaseName数据库名称来区分@Overridepublic void register(String databaseName, DatabaseColleague databaseColleague) {databaseMap.put(databaseName, databaseColleague);}// 中介者 通过不同的数据库名称, 来实现不同数据库的同步数据操作@Overridepublic void SyncData(String databaseName, String data) {if("MainMysql".equals(databaseName)) {DatabaseColleague redis = databaseMap.get("Redis");redis.getDataList().add(data);System.out.println(data + " 同步到Redis数据库中, Redis数据库存储元素: " + redis.getDataList());DatabaseColleague backupMysql = databaseMap.get("BackupMysql");backupMysql.getDataList().add(data);System.out.println(data + " 同步到备份数据库中, 备份数据库存储元素: " + backupMysql.getDataList());} else if ("Redis".equals(databaseName)) {DatabaseColleague mainMysql = databaseMap.get("MainMysql");mainMysql.getDataList().add(data);System.out.println(data + " 同步到主数据库中, 主数据库存储元素: " + mainMysql.getDataList());DatabaseColleague backupMysql = databaseMap.get("BackupMysql");backupMysql.getDataList().add(data);System.out.println(data + " 同步到备份数据库中, 备份数据库存储元素: " + backupMysql.getDataList());}}
}

Colleague(抽象数据库同事类): 组合中介者对象, 通过中介者对象来实现数据同步的方法

abstract class DatabaseColleague {private List<String> dataList; // 用集合表示数据库存储内容protected Mediator mediator; // 中介者对象, 通过中介者对象来实现数据同步的方法public DatabaseColleague(Mediator mediator, String databaseName) {this.mediator = mediator;mediator.register(databaseName, this);dataList = new ArrayList<>();}// 数据库新增数据的方法public abstract void addData(String databaseName, String data);}

ConcreteColleague(具体的数据库同事类): 无需知道(依赖调用)其他数据库同事类, 通过中介者对象来调用实现功能

class MainMysqlColleague extends DatabaseColleague {public MainMysqlColleague(Mediator mediator, String databaseName) {super(mediator, databaseName);}@Overridepublic void addData(String databaseName, String data) {super.getDataList().add(data);System.out.println(data + " 添加到主数据库中, 主数据库存储元素: " + super.getDataList());// 通过中介者对象来实现数据同步功能super.mediator.SyncData(databaseName, data);}
}class RedisColleague extends DatabaseColleague {public RedisColleague(Mediator mediator, String databaseName) {super(mediator, databaseName);}@Overridepublic void addData(String databaseName, String data) {super.getDataList().add(data);System.out.println(data + " 添加到Redis数据库中, Redis数据库存储元素: " + super.getDataList());// 通过中介者对象来实现数据同步功能super.mediator.SyncData(databaseName, data);}
}class BackupMysqlColleague extends DatabaseColleague {public BackupMysqlColleague(Mediator mediator, String databaseName) {super(mediator, databaseName);}
}

测试:

public class MediatorPattern {public static void main(String[] args) {// 创建中介者对象Mediator mediator = new ConcreteMediator();// 创建主数据库同事对象, 依赖中介者对象 和 数据库名称MainMysqlColleague mainMysql = new MainMysqlColleague(mediator, "MainMysql");// 创建Redis数据库同事对象, 依赖中介者对象 和 数据库名称RedisColleague redis = new RedisColleague(mediator, "Redis");// 创建备份数据库同事对象, 依赖中介者对象 和 数据库名称BackupMysqlColleague backupMysql = new BackupMysqlColleague(mediator, "BackupMysql");mainMysql.addData("MainMysql", "张三");System.out.println();redis.addData("Redis", "李四");System.out.println();}
}


中介者模式的注意事项和细节

  • 多个类相互耦合, 会形成网址结构, 使用中介者模式将网状结构分离成星型结构(只依赖中介者), 减少类间的依赖, 进行解耦, 符合迪米特法则
  • 中介者对象承担了主要逻辑责任, 使得对象变得过于复杂

(七) 备忘录模式

备忘录模式(Memento Pattern): 在不破坏封装性的前提下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态, 以便在适当的时候恢复对象

备忘录模式中的角色:

  • Originator: 是一个需要保存状态属性的普通对象, 内部提供一个创建Memento备忘录对象的方法 和 恢复状态的方法
  • Memento: 备忘录对象, 真正负责存储的对象, 存储的内容是 Originator对象的内部状态成员属性
  • Caretaker: 守护者对象, 提供一个容器集合管理多个 Memento对象, 以及提供添加和获取Memento备忘录对象的方法

Memento备忘录对象: 将Originator对象要保存的状态信息 转换为 Memento备忘录对象

class Memento {private String state; // Originator对象要保存的状态信息public Memento(String state) {this.state = state;}
}

Originator: 一个需要保存状态属性的普通对象

class Originator {private String state; // 需要储存的状态信息public Originator(String state) {this.state = state;}// 创建Memento备忘录对象, 将Originator对象要保存的state状态信息 转换为 Memento备忘录对象public Memento crateMemento() {return new Memento(state);}// 通过Memento备忘录对象恢复状态public void recoverStateByMemento(Memento memento) {this.state = memento.getState();}
}

Caretaker守护者对象: 提供一个容器集合管理多个 Memento对象

class Caretaker {private List<Memento> mementos; // 通过集合对象来管理Memento对象public Caretaker() {this.mementos = new ArrayList<>();}// 添加Mement对象到集合public void add(Memento memento) {mementos.add(memento);}// 从集合中获取Memento对象public Memento get(int index) {return mementos.get(index);}
}

测试

public class MementoPattern {public static void main(String[] args) {Caretaker caretaker = new Caretaker();Originator originator = new Originator("状态1");caretaker.add(originator.crateMemento());System.out.println("第一次状态: " + originator.getState());originator.setState("状态2");caretaker.add(originator.crateMemento());System.out.println("第二次状态: " + originator.getState());originator.setState("状态3");caretaker.add(originator.crateMemento());System.out.println("第三次状态: " + originator.getState());System.out.println();originator.recoverStateByMemento(caretaker.get(0));System.out.println("恢复首次状态: " + originator.getState());}
}


备忘录模式的注意事项和细节

  • 给用户提供了一种可以恢复状态的机制, 同时对用户屏蔽了状态保存的细节
  • 备忘录模式的应用场景: 游戏存档、浏览器历史记录回退、数据库事务回滚、编辑器的 Ctrl + Z…
  • 缺点: 需要保存的成员属性过多, 或者频繁地保存状态, 会消耗一定量的内存空间

(八) 解释器模式

在大多数的编程语言中都支持: 运算表达式计算、正则表达式等, 它们都是采用解释器模式实现的, 工作原理大致是: 对一个算术表达式(a+b+c) 通过词法分析其形成词法单元, 而后这些词法单元再通过语法分析器构建语法分析数, 最终形成一个抽象的语法分析树

解释器模式(Interpreter Pattern): 是指 给定一个语言表达式, 定义它的文法(语法规则), 并定义一个解释器, 使得该解释器来解释执行语言表达式

以 四则运算表达式a + b - c 例子来理解 解释器模式:

  • 表达式 a + b - c 首先会转化成字符数组: [‘a’, ‘+’, ‘b’, ‘-’, ‘c’]
  • 字符数组中存在两种类型的字符: 变量(a,b,c) 和 运算符(+, -)
  • 我们需要分别对两种类型的字符分别解析处理

解释器模式模式中存在角色

  • AbstractExpression抽象的表达式: 用来存储表达式中的每一个字符(‘a’, ‘+’, ‘b’, ‘-’, ‘c’), 并提供一个解析处理字符的抽象方法interpret
  • TerminalExpression终结符表达式: 字面上的意思是 存储终结一个表达式的一种字符, 在四则运算表达式中指的是 a、b、c, 即 变量. 重写interpret解释方法(对变量如何处理)
  • NonTerminalExpression非终结符表达式: 刚好相反, 存储非终结一个表达式的一种字符, 在四则运算表达式中指的是 +、-, 即 运算符. 重写interpret解释方法(对运算符如何处理), 每个运算符都只和自己左右两个数字有关系, 但左右不一定都是数字, 也有可能是AbstractExpression表达式, 因此组合左右两个AbstractExpression对象, 最终会形成一个树(类似于二叉树)
  • Context环境上下文角色: 存储解释器之外的全局信息, 以及主要逻辑实现. 如: 接收存储一个表达式(a + b - c), 并将表达式 先转化成字符数组 然后再转化成对应的表达式对象(终结符, 非终结符对象), 最终存储成一个树

Java实现十种行为型模式相关推荐

  1. Java设计模式之行为型模式

    1 设计模式简介 软件设计模式是一套被反复使用的,多数人知晓的.经过分类编写.代码设计经验的总结.它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案,它是解决特定问题的一系列套路, ...

  2. Java设计模式之创建型模式

    一.创建型模式 1.工厂模式[Factory] 定义:工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式. 普通工厂:工厂是具体的,产品是抽象的.[学习难度:★★☆☆☆,使用 ...

  3. 【Java设计模式】006-原型模式

    目录 二.原型模式 1.概述 2.应用场景 3.优缺点 优点 缺点 4.主要角色 5.深克隆与浅克隆 深克隆 浅克隆 6.通过Cloneable接口实现浅克隆 实体类 测试类 运行结果 7.改良5 实 ...

  4. Java经典设计模式-创建型模式-单例模式(Singleton)

    2019独角兽企业重金招聘Python工程师标准>>> 单例模式(Singleton) 单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JV ...

  5. Java经典设计模式-创建型模式-抽象工厂模式(Abstract Factory)

    2019独角兽企业重金招聘Python工程师标准>>> 抽象工厂模式(Abstract Factory) 工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序, ...

  6. java设计模式中不属于创建型模式_23种设计模式第二篇:java工厂模式定义:工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式...

    23种设计模式第二篇:java工厂模式 定义: 工厂模式是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 工厂模式主要是为创建对象提供过渡接口, ...

  7. Java设计模式之五大创建型模式(附实例和详解)

    一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥 ...

  8. Java设计模式之行为型:解释器模式

    一.什么是解释器模式:         解释器模式,就是定义语言的文法,并建立一个解释器来解释该语言中的句子,通过构建解释器,解决某一频繁发生的特定类型问题实例. 这里我们将语言理解成使用规定格式和语 ...

  9. Java设计模式之行为型:访问者模式

    背景: 去医院看病时,医生会给你一个处方单要你去拿药,拿药我们可以分为两步走: (1)去柜台交钱,划价人员会根据处方单上的药进行划价,交钱. (2)去药房拿药,药房工作者同样根据处方单给你相对应的药. ...

最新文章

  1. Attention!注意力机制可解释吗?
  2. 监管发文:规范大学生群体消费贷款,避免陷入消费贷款陷阱
  3. 电子计算机时代 英语,2018年英语专四作文范文:计算机时代
  4. 20 个强大的 Sublime Text 插件
  5. python实现rsa数据加密_python实现RSA与AES混合加密
  6. Windows 2003安装IIS无法复制CONVLOG.EXECONVLOG.EX_问题
  7. 20个最漂亮的基于WordPress的企业网站
  8. 在Ubuntu系统下安装WPS(21.3.2)
  9. 家谱处理(30 分)(字符串的处理substr)
  10. 苹果手机微信记录恢复最简单的恢复方法
  11. 微信开发——加密认证
  12. ORACLE几个关于工作日数的函数(原创)
  13. Vertica资源池
  14. 推特正式起诉马斯克 要求强制其按原协议完成收购
  15. 如何随时远程开机并控制电脑
  16. HTTP学习笔记(适合初学)
  17. 写作小课堂:如何写好工作邮件?
  18. pycharm 无法安装第三方库解决方案
  19. 【干货收藏】测试人员必看的经典书籍
  20. 【深度学习】NIN (Network in Network) 网络

热门文章

  1. java 哲学家_Java哲学家进餐问题|多线程
  2. 紧急提醒!黑客正利用假 ChatGPT 来推送恶意软件
  3. Codeforces 332B Maximum Absurdity(暴力)
  4. 有趣的23000----整理(01)H词根、I词根和J词根
  5. 计算机网络——配置动态路由实验
  6. ie收藏夹在电脑什么位置
  7. 嵌入式计算机的关键特征,与通用计算机的区别
  8. Scrapy爬虫项目的管理部署
  9. 【cv君个人整理学习路线】视觉算法从入门到进阶
  10. 台式电脑计算机被限制,gpedit.msc 无法运行 提示本次操作由于这台计算机的限制而被取消(超强解决方案)...