设计模式—行为型模式
行为性模式有以下几种模式:
- 模版方法模式
- 命令模式
- 访问者模式
- 迭代器模式
- 观察者模式
- 中介者模式
- 备忘录模式
- 解释器模式(Interpreter模式)
- 状态模式
- 策略模式
- 职责链模式(责任链模式)。
模板方法模式
基本介绍
- 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
- 简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
- 这种类型的设计模式属于行为型模式。
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZcwXAOB-1611137918409)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210118113313479.png)]
原理类图说明
- AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法operationr2,3,4
- ConcreteClass 实现抽象方法operationr2,3,4, 以完算法中特点子类的步骤
模板方法模式的钩子方法
在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
案例理解
编写制作豆浆的程序,说明如下:
- 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
- 通过添加不同的配料,可以制作出不同口味的豆浆
- 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
- 需要制作纯豆浆,使用钩子方法判断是否需要加料
请使用 模板方法模式 完成 (说明:因为模板方法模式,比较简单,很容易就想到这个方案,因此就直接使用,不再使用传统的方案来引出模板方法模式 )
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QjfSnjL8-1611137918412)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210118113633455.png)]
代码实现
//SoyMilk 抽象类,提供模板方法
public abstract class SoyMilk {
//模板方法,定义执行该抽象类方法的模板
//使用final修饰,阻止子类对他进行重写
final void make(){
select();
if (whetherAdd()) {
add();
}
soak();
beat();
}
void select(){
System.out.println("选择上等的黄豆");
}
//添加材料方法,需要子类实现
abstract void add();
void soak(){
System.out.println("材料浸泡一段时间");
}
void beat(){
System.out.println("开始打豆浆");
}
//钩子方法,用于判断是否需要加料,默认为true,子类可以重写覆盖方法
boolean whetherAdd(){
return true;
}
}
//BlackBeanSoyMilk
public class BlackBeanSoyMilk extends SoyMilk {
@Override
void add() {
System.out.println("加入上等的黑豆");
}
}
//RedBeanSoyMilk
public class RedBeanSoyMilk extends SoyMilk {
@Override
void add() {
System.out.println("加入上好的红豆");
}
}
//PureSoyMilk
public class PureSoyMilk extends SoyMilk {
//不需要加料时,默认空实现
@Override
void add() {
}
//不需要加料,需要重写钩子方法
@Override
boolean whetherAdd() {
return false;
}
}
//Client
public class Client {
public static void main(String[] args) {
System.out.println("----------制作黑豆豆浆----------");
SoyMilk blackBeanSoyMilk = new BlackBeanSoyMilk();
blackBeanSoyMilk.make();
System.out.println("----------制作红豆豆浆----------");
SoyMilk redBeanSoyMilk = new RedBeanSoyMilk();
redBeanSoyMilk.make();
System.out.println("----------制作纯豆浆----------");
SoyMilk pureSoyMilk = new PureSoyMilk();
pureSoyMilk.make();
}
}
优缺点
- 基本思想是:**算法只存在于一个地方,也就是在父类中,容易修改。**需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
- **实现了最大化代码复用。**父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
- **既统一了算法,也提供了很大的灵活性。**父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
- 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
- 一般模板方法都加上final关键字, 防止子类重写模板方法.
- 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理
模板方法模式在Spring框架应用的源码分析
Spring IOC容器初始化时运用到的模板方法模式
命令模式
基本介绍
- 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
- 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
- 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
- 通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cww08abk-1611137918414)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210118152956460.png)]
原理类图说明
- Invoker 是调用者角色
- Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
- Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
- ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute
案例理解
看一个具体的需求
- 我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制对这些家电工作。
- 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App,分别控制,我们希望只要一个app就可以控制全部智能家电。
- 要实现一个app控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给app调用,这时 就可以考虑使用命令模式。
- 命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来.
- 在例子中,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b937T3I1-1611137918416)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210118153243023.png)]
代码实现
//抽象的命令类Command
public interface Command {
void execute();
void undo();
}
//接受执行者LightReceiver
public class LightReceiver{
public void on(){
System.out.println("灯打开");
}
public void off(){
System.out.println("灯关闭");
}
}
//命令实现类LightOnCommand
public class LightOnCommand implements Command {
private LightReceiver light;
public LightOnCommand(LightReceiver light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
//命令实现类LightOffCommand
public class LightOffCommand implements Command {
private LightReceiver light;
public LightOffCommand(LightReceiver light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
//空命令类NoCommand
/**
* 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做
* 其实,这样是一种设计模式, 可以省掉对空判断
* @author Administrator
*
*/
public class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
//指挥者RemoteController
public class RemoteController {
private Command onCommands[];
private Command offCommands[];
private Command unDoCommand;
public RemoteController(){
onCommands=new Command[5];
offCommands=new Command[5];
for(int i=0;i<5;i++){
onCommands[i]=new NoCommand();
offCommands[i]=new NoCommand();
}
}
public void setCommand(int on,Command onCommand,Command offCommand){
this.onCommands[on]=onCommand;
this.offCommands[on]=offCommand;
}
public void clickOnButton(int on){
onCommands[on].execute();
unDoCommand=onCommands[on];
}
public void clickOffButton(int on){
offCommands[on].execute();
unDoCommand=offCommands[on];
}
public void repeal(){
if(unDoCommand!=null){
unDoCommand.undo();
}
}
}
//测试Client
public class Client {
public static void main(String[] args) {
LightReceiver lightReceiver=new LightReceiver();
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
RemoteController remoteController=new RemoteController();
remoteController.setCommand(0,lightOnCommand,lightOffCommand);
remoteController.clickOnButton(0);
remoteController.clickOffButton(0);
remoteController.repeal();
}
}
//当我们新增一个其它家具时,只需要新增家具Receiver类,on和offCommand类就行,不需要修改指挥者类,扩展性还行。
优缺点
- 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
- 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
- 容易实现对请求的撤销和重做
- 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
- 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
- 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD(DOS命令) 订单的撤销/恢复、触发-反馈机制
命令模式在Spring框架JdbcTemplate应用的源码分析
Spring框架的JdbcTemplate就使用到了命令模式
访问者模式
基本介绍
- 访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
- 主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
- **访问者模式的基本工作原理是:**在被访问的类里面加一个对外提供接待访问者的接口
- **访问者模式主要应用场景是:**需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WqGJTF4J-1611137918418)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210118175248749.png)]
原理类图说明
- Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作
- ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是每个操作实现的部分.
- ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
- Element 定义一个accept 方法,接收一个访问者对象
- ConcreteElement 为具体元素,实现了accept 方法
案例理解
完成测评系统需求
将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等)
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RzFDhaPY-1611137918419)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210118180247266.png)]
代码实现
这里Action和Person使用了双分派模式:
所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
//被访问者抽象类Person
public abstract class Person {
public abstract void accept(Action action);
}
//被访问者具体类Man
public class Man extends Person {
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
//被访问者具体类Women
public class Women extends Person {
@Override
public void accept(Action action) {
action.getWomenResult(this);
}
}
//访问抽象类Action
public abstract class Action {
public abstract void getManResult(Man man);
public abstract void getWomenResult(Women women);
}
//访问具体类Success
public class Success extends Action {
@Override
public void getManResult(Man man) {
System.out.println("男人的肯定");
}
@Override
public void getWomenResult(Women women) {
System.out.println("女人的认可");
}
}
//访问具体类Fail
public class Fail extends Action {
@Override
public void getManResult(Man man) {
System.out.println("男人的摇头");
}
@Override
public void getWomenResult(Women women) {
System.out.println("女人的头疼");
}
}
//保存被访问的元素,用来允许访问者访问元素
public class ObjectStructure {
private List<Person> elements=new LinkedList<>();
public void attach(Person person){
elements.add(person);
}
public void detach(Person person){
elements.remove(person);
}
public void display(Action action){
for(Person p:elements){
p.accept(action);
}
}
}
//测试
public class Client {
public static void main(String[] args) {
ObjectStructure objectStructure=new ObjectStructure();
Man man = new Man();
objectStructure.attach(man);
objectStructure.display(new Success());
objectStructure.detach(man);
Women women=new Women();
objectStructure.attach(women);
objectStructure.display(new Fail());
}
}
//假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需增加一个Action子类即可在客户端调用即可,不需要改动任何其他类的代码。
优缺点
优点
- 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
- 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点
- 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
- 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
- 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的
迭代器模式
基本介绍
- 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
- 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
- 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。(简单来说就是给对象提供一个迭代器)
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6T7LegKt-1611137918420)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119111222864.png)]
原理类图说明
- lterator :迭代器接口,是系统提供,含义hasNext, next, removeConcretelIterator:具体的迭代器类,管理迭代
- Aggregate :一个统一的聚合接口,将客户端和具体聚合解耦
- ConcreteAggreage :具体的聚合持有对象集合,并提供一个方法,返回一个迭代器,该迭代器可以正确遍历集合
- Client :客户端,通过Iterator和Aggregate依赖子类
案例理解
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fwOuc6L9-1611137918421)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119111810315.png)]
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbWlc5CI-1611137918422)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119112058528.png)]
代码实现
//系
public class Department {
private String name;
public Department(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Department{" +
"name='" + name + '\'' +
'}';
}
}
//迭代器类ArtCollegeIterator
public class ArtCollegeIterator implements Iterator {
private List<Department> departments;
private int num=-1;
public ArtCollegeIterator(List<Department> departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (num>=departments.size()-1){
return false;
}
num++;
return true;
}
@Override
public Object next() {
Department department = departments.get(num);
return department;
}
@Override
public void remove() {
}
}
//迭代器类ComputerCollegeIterator
public class ComputerCollegeIterator implements Iterator {
private Department departments[];//数组形式
private int num=0;
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if(departments[num]!=null){
return true;
}
return false;
}
@Override
public Object next() {
Department department=departments[num];
num++;
return department;
}
@Override
public void remove() {
}
}
//学院抽象类College
public interface College {
public String getName();
public void addDepartment(String name);
public Iterator getIterator();
}
//计算机学院ComputerCollege
public class ComputerCollege implements College {
private Department departments[];
private int num=0;
public ComputerCollege() {
this.departments = new Department[5];
addDepartment("软件工程");
addDepartment("信息工程");
addDepartment("网络工程");
}
@Override
public String getName() {
return "信息学院";
}
@Override
public void addDepartment(String name) {
Department department=new Department(name);
departments[num]=department;
num++;
}
@Override
public Iterator getIterator() {
return new ComputerCollegeIterator(departments);
}
}
//艺术学院ArtCollege
public class ArtCollege implements College {
private List<Department> departments;
public ArtCollege() {
this.departments = new ArrayList<>();
addDepartment("服装工程");
addDepartment("室内设计");
addDepartment("画画工程");
}
@Override
public String getName() {
return "艺术系";
}
@Override
public void addDepartment(String name) {
departments.add(new Department(name));
}
@Override
public Iterator getIterator() {
return new ArtCollegeIterator(departments);
}
}
//输出类OutputImpl
public class OutputImpl {
private List<College> colleges;
public OutputImpl(List<College> colleges) {
this.colleges = colleges;
}
public void print(){
Iterator<College> iterator = colleges.iterator();
while (iterator.hasNext()){
College c = iterator.next();
System.out.println("--------"+c.getName()+"---------");
printDepartments(c.getIterator());
}
}
public void printDepartments(Iterator iterator){
while(iterator.hasNext()){
Department department = (Department) iterator.next();
System.out.println(department.getName());
}
}
}
//测试类
public class Client {
public static void main(String[] args) {
ComputerCollege computerCollege=new ComputerCollege();
ArtCollege artCollege=new ArtCollege();
List<College> list=new ArrayList<>();
list.add(computerCollege);
list.add(artCollege);
OutputImpl output=new OutputImpl(list);
output.print();
}
}
优缺点
优点
- 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
- 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
- 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
- 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
缺点
- 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类
迭代器模式在JDK-ArrayList集合应用的源码分析
JDK的ArrayList 集合中就使用了迭代器模式
观察者模式
基本介绍
- 观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当主体的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用。
- 观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
- 观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
- 主体三个核心方法:
- registerObserver 注册
- removeObserver 移除
- notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定
案例理解
- 天气预报项目需求,具体要求如下:
- 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
- 需要设计开放型API,便于其他第三方也能接入气象站获取数据。
- 提供温度、气压和湿度的接口
- 测量数据更新时,要能实时的通知给第三方
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q1BQCNx8-1611137918424)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119144123973.png)]
代码实现
//观察者接口类
public interface Observer {
public void update(int temperature,int pressure,int humidity);
}
//被观察者(主体)接口类
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObserver();
}
//观察者具体类(多个)
public class BaiDu implements Observer {
private int temperature;
private int pressure;
private int humidity;
@Override
public void update(int temperature, int pressure, int humidity) {
this.temperature=temperature;
this.pressure=pressure;
this.humidity=humidity;
display();
}
public void display(){
System.out.println("=========百度网站==========");
System.out.println("百度网站今日温度"+this.temperature);
System.out.println("百度网站今日气压"+this.pressure);
System.out.println("百度网站今日湿度"+this.humidity);
}
}
被观察者(主体)具体类(一个)
public class WeatherData implements Subject{
private int temperature;
private int pressure;
private int humidity;
//观察者集合
private List<Observer> observerList;
public WeatherData() {
this.observerList =new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observerList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if(observerList.contains(observer)){
observerList.remove(observer);
}
}
@Override
public void notifyObserver() {
for(Observer observer:observerList){
if (observer!=null) {
observer.update(temperature, pressure, humidity);
}
}
}
public void setData(int temperature,int pressure,int humidity){
this.temperature=temperature;
this.pressure=pressure;
this.humidity=humidity;
System.out.println("----主体类天气数据更新----");
notifyObserver();
}
}
//测试
public class Client {
public static void main(String[] args) {
BaiDu baiDu=new BaiDu();
WeatherData weatherData=new WeatherData();
weatherData.registerObserver(baiDu);
weatherData.registerObserver(new WeChat());
weatherData.setData(1,2,3);
}
}
//在观察者模式下,增加一个观察者,十分方便,只需要把观察者加到主体的List就能实现
优缺点
优点
- 观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。
- 增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心主体类不会修改代码,遵守了ocp原则。
缺点
- 在应用观察者模式时需要考虑一下开发小路问题,程序中包括一个被观察者和多个被观察者,开发和调试比较复杂,而且Java中的消息的通知默认是顺序执行的,一个观察者的卡顿会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。
观察者模式在Jdk应用的源码分析
jdk的Observable类就使用了观察者模式
中介者模式
基本介绍
- 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
- 中介者模式属于行为型模式,使代码易于维护
- 比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的中介者,在前后端交互时起到了中间人的作用
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xu89kJ5P-1611137918425)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119161820420.png)]
原理类图说明
- Mediator就是抽象中介者,定义了同事对象到中介者对象的接口
- Colleague是抽象同事类
- ConcreteMediator具体的中介者对象,实现抽象方法,他需要知道所有的具体的同事类,即以一个集合来管理HashMap,并接受某个同事对象消息,完成相应的任务
- ConcreteColleague具体的同事类,会有很多,每个同事只知道自己的行为,而不了解其他同事类的行为(方法),但是他们都依赖中介者对象
案例理解
智能家庭项目
- 智能家庭包括各种设备,闹钟、咖啡机、电视机、窗帘 等
- 主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为:闹铃响起->咖啡机开始做咖啡->窗帘自动落下->电视机开始播放
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z4fF3Y2I-1611137918426)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119162435313.png)]
代码实现
//抽象中介类Mediator
public abstract class Mediator {
//将给中介者对象,加入到集合中
public abstract void register(String colleagueName, Colleague colleague);
//接收消息, 具体的同事对象发出
public abstract void getMessage(int stateChange, String colleagueName);
public abstract void sendMessage();
}
//具体中介类ConcreteMediator
public class ConcreteMediator extends Mediator {
//集合,放入所有的同事对象
private HashMap<String, Colleague> colleagueMap;
private HashMap<String, String> interMap;
public ConcreteMediator() {
colleagueMap = new HashMap<String, Colleague>();
interMap = new HashMap<String, String>();
}
@Override
public void register(String colleagueName, Colleague colleague) {
// TODO Auto-generated method stub
colleagueMap.put(colleagueName, colleague);
// TODO Auto-generated method stub
if (colleague instanceof Alarm) {
interMap.put("Alarm", colleagueName);
} else if (colleague instanceof CoffeeMachine) {
interMap.put("CoffeeMachine", colleagueName);
} else if (colleague instanceof TV) {
interMap.put("TV", colleagueName);
} else if (colleague instanceof Curtains) {
interMap.put("Curtains", colleagueName);
}
}
@Override
public void getMessage(int stateChange, String colleagueName) {
// TODO Auto-generated method stub
//处理闹钟发出的消息
if (colleagueMap.get(colleagueName) instanceof Alarm) {
if (stateChange == 0) {
((CoffeeMachine) (colleagueMap.get(interMap
.get("CoffeeMachine")))).on();
((TV) (colleagueMap.get(interMap.get("TV")))).startTv();
} else if (stateChange == 1) {
((TV) (colleagueMap.get(interMap.get("TV")))).stopTv();
}
} else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
.up();
} else if (colleagueMap.get(colleagueName) instanceof TV) {//如果TV发现消息
} else if (colleagueMap.get(colleagueName) instanceof Curtains) {
//如果是以窗帘发出的消息,这里处理...
}
}
@Override
public void sendMessage() {
}
}
//抽象同事类
public abstract class Colleague {
private String name;
private Mediator mediator;
public Colleague(String name, Mediator mediator) {
this.name = name;
this.mediator = mediator;
}
public Mediator getMediator(){
return this.mediator;
}
public abstract void sendMessage(int state);
}
//具体同事类
public class Alarm extends Colleague {
public Alarm(String name, Mediator mediator) {
super(name, mediator);
mediator.register("Alarm",this);
}
@Override
public void sendMessage(int state) {
this.getMediator().getMessage(state,"Alarm");
}
public void alarm(int state){
sendMessage(state);
}
}
---
public class TV extends Colleague{
public TV(String name, Mediator mediator) {
super(name, mediator);
mediator.register("TV",this);
}
@Override
public void sendMessage(int state) {
// TODO Auto-generated method stub
this.getMediator().getMessage(state,"TV");
}
public void startTv() {
// TODO Auto-generated method stub
System.out.println("It's time to StartTv!");
}
public void stopTv() {
// TODO Auto-generated method stub
System.out.println("StopTv!");
}
}
//同事类等等。。。
//测试Client
public class Client {
public static void main(String[] args) {
//创建一个中介者对象
Mediator mediator = new ConcreteMediator();
//创建Alarm 并且加入到 ConcreteMediator 对象的HashMap
Alarm alarm = new Alarm("alarm",mediator);
//创建了CoffeeMachine 对象,并 且加入到 ConcreteMediator 对象的HashMap
CoffeeMachine coffeeMachine = new CoffeeMachine(
"coffeeMachine",mediator);
//创建 Curtains , 并 且加入到 ConcreteMediator 对象的HashMap
Curtains curtains = new Curtains("curtains",mediator);
TV tV = new TV("TV",mediator);
//让闹钟发出消息
alarm.alarm(0);
coffeeMachine.on();
alarm.alarm(1);
}
}
//总结来说,就是创建一个中介类,维护所有的其它需要调用的类,类的调用通过中介指定,而不通过类与类之间的调用
优缺点
- 多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
- 减少类间依赖,降低了耦合,符合迪米特原则
- 中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
- 如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意
备忘录模式
基本介绍
- 备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
- 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HW8Meas7-1611137918427)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119175428655.png)]
原理类图说明
- originator : 对象(需要保存状态的对象)
- Memento : 备忘录对象,负责保存好记录,即Originator内部状态
- Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率
说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要要 HashMap <String, 集合>
案例理解
游戏角色状态恢复问题
游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态
传统方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kkxr7ti5-1611137918429)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119175637380.png)]
传统的方式的问题分析
- 一个对象,就对应一个保存对象状态的对象, 这样当我们游戏的对象很多时,不利于管理,开销也很大.
- 传统的方式是简单地做备份,new出另外一个对象出来,再把需要备份的数据放到这个新对象,但这就暴露了对象内部的细节
备忘录模式
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g8CTV9Jt-1611137918430)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210119175854837.png)]
代码实现
//需要使用备忘录的游戏角色
public class GameRole {
private int vit;
private int def;
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
//保存到备忘录
public Memento getMemento(){
return new Memento(vit,def);
}
//返回备忘录状态
public void getInfoFromMemento(Memento memento){
this.vit=memento.getVit();
this.def=memento.getDef();
}
//显示状态
public void display(){
System.out.println("游戏角色当前的攻击力:" + this.vit + " 防御力: " + this.def);
}
}
//Memento 备忘录对象
public class Memento {
private int vit;
private int def;
public Memento(int vit, int def) {
this.vit = vit;
this.def = def;
}
public int getVit() {
return vit;
}
public int getDef() {
return def;
}
}
//CareTaker 用于保存备忘录的类
public class CareTaker {
//如果只保存一次状态
private Memento memento;
//对GameRole 保存多次状态
//private ArrayList<Memento> mementos;
//对多个游戏角色保存多个状态
//private HashMap<String, ArrayList<Memento>> rolesMementos;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
//测试Client
public class Client {
public static void main(String[] args) {
GameRole gameRole=new GameRole();
gameRole.setDef(100);
gameRole.setVit(100);
gameRole.display();
CareTaker careTaker=new CareTaker();
careTaker.setMemento(gameRole.getMemento());
gameRole.setVit(20);
gameRole.setVit(10);
gameRole.display();
gameRole.getInfoFromMemento(careTaker.getMemento());
gameRole.display();
}
}
优缺点
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
- 实现了信息的封装,使得用户不需要关心状态的保存细节
- 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意
- 适用的应用场景:1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理
- 为了节约内存,备忘录模式可以和原型模式配合使用
解释器模式
基本介绍
- 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
- 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
- 应用场景
- 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 一些重复出现的问题可以用一种简单的语言来表达
- 一个简单语法需要解释的场景
这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZyh9tGP-1611137918431)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120112618221.png)]
原理类图说明
- Context: 是环境角色,含有解释器之外的全局信息.
- AbstractExpression: 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的点所共享
- TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作
- NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作.
说明: 输入Context he TerminalExpression 信息通过Client 输入即可
案例理解
通过解释器模式来实现四则运算
- 先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复
- 在分别输入a ,b, c, d, e 的值
- 最后求出结果:如图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6p3RBKKe-1611137918432)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120112829318.png)]
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TEr0BVqJ-1611137918433)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120113000307.png)]
代码实现
/**
* 抽象类表达式,通过HashMap 键值对, 可以获取到变量的值
*
* @author Administrator
*
*/
public abstract class Expression {
// a + b - c
// 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value就是就是具体值
// HashMap {a=10, b=20}
public abstract int interpreter(HashMap<String, Integer> var);
}
/**
* 变量的解释器
* @author Administrator
*
*/
public class VarExpression extends Expression {
private String key; // key=a,key=b,key=c
public VarExpression(String key) {
this.key = key;
}
// var 就是{a=10, b=20}
// interpreter 根据 变量名称,返回对应值
@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}
/**
* 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系,
* 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类
*
* @author Administrator
*
*/
public class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
//因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
@Override
public int interpreter(HashMap<String, Integer> var) {
// TODO Auto-generated method stub
return 0;
}
}
//处理加法的解释器
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
//处理相加
//var 仍然是 {a=10,b=20}..
//一会我们debug 源码,就ok
public int interpreter(HashMap<String, Integer> var) {
//super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10
//super.right.interpreter(var): 返回right 表达式对应值 b = 20
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
//处理减法的解释器
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
//求出left 和 right 表达式相减后的结果
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
//计算器Calculator
public class Calculator {
// 定义表达式
private Expression expression;
// 构造函数传参,并解析
public Calculator(String expStr) { // expStr = a+b
// 安排运算先后顺序
Stack<Expression> stack = new Stack<>();
// 表达式拆分成字符数组
char[] charArray = expStr.toCharArray();// [a, +, b]
Expression left = null;
Expression right = null;
//遍历我们的字符数组, 即遍历 [a, +, b]
//针对不同的情况,做处理
for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '+': //
left = stack.pop();// 从stack取出left => "a"
right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
stack.push(new AddExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack
break;
case '-': //
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
default:
//如果是一个 Var 就创建要给 VarExpression 对象,并push到 stack
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
//当遍历完整个 charArray 数组后,stack 就得到最后Expression
this.expression = stack.pop();
}
public int run(HashMap<String, Integer> var) {
//最后将表达式a+b和 var = {a=10,b=20}
//然后传递给expression的interpreter进行解释执行
return this.expression.interpreter(var);
}
}
//client测试
public class ClientTest {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String expStr = getExpStr(); // a+b
HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
}
// 获得表达式
public static String getExpStr() throws IOException {
System.out.print("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
// 获得值映射
public static HashMap<String, Integer> getValue(String expStr) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
for (char ch : expStr.toCharArray()) {
if (ch != '+' && ch != '-') {
if (!map.containsKey(String.valueOf(ch))) {
System.out.print("请输入" + String.valueOf(ch) + "的值:");
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
}
优缺点
- 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
- 应用场景:编译器、运算表达式计算、正则表达式、机器人等
- 使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低
解释器模式在Spring框架应用的源码剖析
解释器模式在Spring框架应用的源码剖析
状态模式
基本介绍
- 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vFxtqukE-1611137918434)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120145427991.png)]
原理类图说明
- Context 类为环境角色, 用于维护State实例,这个实例定义当前状态
- State 是抽象状态角色,定义一个接口封装与Context 的一个特点接口相关行为
- ConcreteState 具体的状态角色,每个子类实现一个与Context 的一个状态相关行为
案例理解
APP抽奖活动问题
- 假如每参加一次这个活动要扣除用户50积分,中奖概率是10%
- 奖品数量固定,抽完就不能抽奖
- 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完
- 活动的四个状态转换关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8FFmbq71-1611137918436)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120145738534.png)]
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RnkEcYN0-1611137918437)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120145937404.png)]
代码实现
/**
* 状态抽象类
* @author Administrator
*
*/
public abstract class State {
// 扣除积分 - 50
public abstract void deductMoney();
// 是否抽中奖品
public abstract boolean raffle();
// 发放奖品
public abstract void dispensePrize();
}
/**
* 不能抽奖状态
* @author Administrator
*
*/
public class NoRaffleState extends State {
// 初始化时传入活动引用,扣除积分后改变其状态
RaffleActivity activity;
public NoRaffleState(RaffleActivity activity) {
this.activity = activity;
}
// 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态
@Override
public void deductMoney() {
System.out.println("扣除50积分成功,您可以抽奖了");
activity.setState(activity.getCanRaffleState());
}
// 当前状态不能抽奖
@Override
public boolean raffle() {
System.out.println("扣了积分才能抽奖喔!");
return false;
}
// 当前状态不能发奖品
@Override
public void dispensePrize() {
System.out.println("不能发放奖品");
}
}
/**
* 可以抽奖的状态
* @author Administrator
*
*/
public class CanRaffleState extends State {
RaffleActivity activity;
public CanRaffleState(RaffleActivity activity) {
this.activity = activity;
}
//已经扣除了积分,不能再扣
@Override
public void deductMoney() {
System.out.println("已经扣取过了积分");
}
//可以抽奖, 抽完奖后,根据实际情况,改成新的状态
@Override
public boolean raffle() {
System.out.println("正在抽奖,请稍等!");
Random r = new Random();
int num = r.nextInt(10);
// 10%中奖机会
if(num == 0){
// 改变活动状态为发放奖品 context
activity.setState(activity.getDispenseState());
return true;
}else{
System.out.println("很遗憾没有抽中奖品!");
// 改变状态为不能抽奖
activity.setState(activity.getNoRafflleState());
return false;
}
}
// 不能发放奖品
@Override
public void dispensePrize() {
System.out.println("没中奖,不能发放奖品");
}
}
/**
* 发放奖品的状态
* @author Administrator
*
*/
public class DispenseState extends State {
// 初始化时传入活动引用,发放奖品后改变其状态
RaffleActivity activity;
public DispenseState(RaffleActivity activity) {
this.activity = activity;
}
//
@Override
public void deductMoney() {
System.out.println("不能扣除积分");
}
@Override
public boolean raffle() {
System.out.println("不能抽奖");
return false;
}
//发放奖品
@Override
public void dispensePrize() {
if(activity.getCount() > 0){
System.out.println("恭喜中奖了");
// 改变状态为不能抽奖
activity.setState(activity.getNoRafflleState());
}else{
System.out.println("很遗憾,奖品发送完了");
// 改变状态为奖品发送完毕, 后面我们就不可以抽奖
activity.setState(activity.getDispensOutState());
//System.out.println("抽奖活动结束");
//System.exit(0);
}
}
}
/**
* 奖品发放完毕状态
* 说明,当我们activity 改变成 DispenseOutState, 抽奖活动结束
* @author Administrator
*
*/
public class DispenseOutState extends State {
// 初始化时传入活动引用
RaffleActivity activity;
public DispenseOutState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void deductMoney() {
System.out.println("奖品发送完了,请下次再参加");
}
@Override
public boolean raffle() {
System.out.println("奖品发送完了,请下次再参加");
return false;
}
@Override
public void dispensePrize() {
System.out.println("奖品发送完了,请下次再参加");
}
}
/**
* 抽奖活动 //
*
* @author Administrator
*
*/
public class RaffleActivity {
// state 表示活动当前的状态,是变化
State state = null;
// 奖品数量
int count = 0;
// 四个属性,表示四种状态
State noRafflleState = new NoRaffleState(this);
State canRaffleState = new CanRaffleState(this);
State dispenseState = new DispenseState(this);
State dispensOutState = new DispenseOutState(this);
//构造器
//1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)
//2. 初始化奖品的数量
public RaffleActivity( int count) {
this.state = getNoRafflleState();
this.count = count;
}
//扣分, 调用当前状态的 deductMoney
public void debuctMoney(){
state.deductMoney();
}
//抽奖
public void raffle(){
// 如果当前的状态是抽奖成功
if(state.raffle()){
//领取奖品
state.dispensePrize();
}
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
//这里请大家注意,每领取一次奖品,count--
public int getCount() {
int curCount = count;
count--;
return curCount;
}
public void setCount(int count) {
this.count = count;
}
public State getNoRafflleState() {
return noRafflleState;
}
public void setNoRafflleState(State noRafflleState) {
this.noRafflleState = noRafflleState;
}
public State getCanRaffleState() {
return canRaffleState;
}
public void setCanRaffleState(State canRaffleState) {
this.canRaffleState = canRaffleState;
}
public State getDispenseState() {
return dispenseState;
}
public void setDispenseState(State dispenseState) {
this.dispenseState = dispenseState;
}
public State getDispensOutState() {
return dispensOutState;
}
public void setDispensOutState(State dispensOutState) {
this.dispensOutState = dispensOutState;
}
}
/**
* 状态模式测试类
* @author Administrator
*
*/
public class ClientTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建活动对象,奖品有1个奖品
RaffleActivity activity = new RaffleActivity(1);
// 我们连续抽300次奖
for (int i = 0; i < 30; i++) {
System.out.println("--------第" + (i + 1) + "次抽奖----------");
// 参加抽奖,第一步点击扣除积分
activity.debuctMoney();
// 第二步抽奖
activity.raffle();
}
}
}
借贷平台案例 源码剖析
借贷平台的订单,有审核-发布-抢单 等等 步骤,随着操作的不同,会改变订单的状态, 项目中的这个模块实现就会使用到状态模式
/**
* 状态接口
* @author Administrator
*
*/
public interface State {
/**
* 电审
*/
void checkEvent(Context context);
/**
* 电审失败
*/
void checkFailEvent(Context context);
/**
* 定价发布
*/
void makePriceEvent(Context context);
/**
* 接单
*/
void acceptOrderEvent(Context context);
/**
* 无人接单失效
*/
void notPeopleAcceptEvent(Context context);
/**
* 付款
*/
void payOrderEvent(Context context);
/**
* 接单有人支付失效
*/
void orderFailureEvent(Context context);
/**
* 反馈
*/
void feedBackEvent(Context context);
String getCurrentState();
}
public abstract class AbstractState implements State {
protected static final RuntimeException EXCEPTION = new RuntimeException("操作流程不允许");
//抽象类,默认实现了 State 接口的所有方法
//该类的所有方法,其子类(具体的状态类),可以有选择的进行重写
@Override
public void checkEvent(Context context) {
throw EXCEPTION;
}
@Override
public void checkFailEvent(Context context) {
throw EXCEPTION;
}
@Override
public void makePriceEvent(Context context) {
throw EXCEPTION;
}
@Override
public void acceptOrderEvent(Context context) {
throw EXCEPTION;
}
@Override
public void notPeopleAcceptEvent(Context context) {
throw EXCEPTION;
}
@Override
public void payOrderEvent(Context context) {
throw EXCEPTION;
}
@Override
public void orderFailureEvent(Context context) {
throw EXCEPTION;
}
@Override
public void feedBackEvent(Context context) {
throw EXCEPTION;
}
}
//各种具体状态类
class FeedBackState extends AbstractState {
@Override
public String getCurrentState() {
return StateEnum.FEED_BACKED.getValue();
}
}
class GenerateState extends AbstractState {
@Override
public void checkEvent(Context context) {
context.setState(new ReviewState());
}
@Override
public void checkFailEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.GENERATE.getValue();
}
}
class NotPayState extends AbstractState {
@Override
public void payOrderEvent(Context context) {
context.setState(new PaidState());
}
@Override
public void feedBackEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.NOT_PAY.getValue();
}
}
class PaidState extends AbstractState {
@Override
public void feedBackEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.PAID.getValue();
}
}
class PublishState extends AbstractState {
@Override
public void acceptOrderEvent(Context context) {
//把当前状态设置为 NotPayState。。。
//至于应该变成哪个状态,有流程图来决定
context.setState(new NotPayState());
}
@Override
public void notPeopleAcceptEvent(Context context) {
context.setState(new FeedBackState());
}
@Override
public String getCurrentState() {
return StateEnum.PUBLISHED.getValue();
}
}
class ReviewState extends AbstractState {
@Override
public void makePriceEvent(Context context) {
context.setState(new PublishState());
}
@Override
public String getCurrentState() {
return StateEnum.REVIEWED.getValue();
}
}
/**
* 状态枚举类
* @author Administrator
*
*/
public enum StateEnum {
//订单生成
GENERATE(1, "GENERATE"),
//已审核
REVIEWED(2, "REVIEWED"),
//已发布
PUBLISHED(3, "PUBLISHED"),
//待付款
NOT_PAY(4, "NOT_PAY"),
//已付款
PAID(5, "PAID"),
//已完结
FEED_BACKED(6, "FEED_BACKED");
private int key;
private String value;
StateEnum(int key, String value) {
this.key = key;
this.value = value;
}
public int getKey() {return key;}
public String getValue() {return value;}
}
//环境上下文
public class Context extends AbstractState{
//当前的状态 state, 根据我们的业务流程处理,不停的变化
private State state;
@Override
public void checkEvent(Context context) {
state.checkEvent(this);
getCurrentState();
}
@Override
public void checkFailEvent(Context context) {
state.checkFailEvent(this);
getCurrentState();
}
@Override
public void makePriceEvent(Context context) {
state.makePriceEvent(this);
getCurrentState();
}
@Override
public void acceptOrderEvent(Context context) {
state.acceptOrderEvent(this);
getCurrentState();
}
@Override
public void notPeopleAcceptEvent(Context context) {
state.notPeopleAcceptEvent(this);
getCurrentState();
}
@Override
public void payOrderEvent(Context context) {
state.payOrderEvent(this);
getCurrentState();
}
@Override
public void orderFailureEvent(Context context) {
state.orderFailureEvent(this);
getCurrentState();
}
@Override
public void feedBackEvent(Context context) {
state.feedBackEvent(this);
getCurrentState();
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
@Override
public String getCurrentState() {
System.out.println("当前状态 : " + state.getCurrentState());
return state.getCurrentState();
}
}
/**测试类*/
public class ClientTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建context 对象
Context context = new Context();
//将当前状态设置为 PublishState
context.setState(new PublishState());
System.out.println(context.getCurrentState());
// //publish --> not pay
context.acceptOrderEvent(context);
// //not pay --> paid
context.payOrderEvent(context);
// // 失败, 检测失败时,会抛出异常
// try {
// context.checkFailEvent(context);
// System.out.println("流程正常..");
// } catch (Exception e) {
// // TODO: handle exception
// System.out.println(e.getMessage());
// }
}
}
优缺点
- 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
- 方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else语句,而且容易出错
- 符合“开闭原则”。容易增删状态
- 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
- 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式
策略模式
基本介绍
- 策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
- 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l9UbZRek-1611137918439)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120162310483.png)]
原理类图说明
从上图可以看到,客户context 有成员变量strategy或者其他的策略接口,至于需要使用到哪个策略,我们可以在构造器中指定
案例理解
编写鸭子项目,具体要求如下:
- 有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等)
- 显示鸭子的信息
传统方式
传统方式类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uv1v9Bu-1611137918440)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120162444357.png)]
传统的方式实现的问题
- 其它鸭子,都继承了Duck类,所以fly让所有子类都会飞了,这是不正确的
- 上面说的1 的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。会有溢出效应
- 为了改进1问题,我们可以通过覆盖fly 方法来解决 => 覆盖解决
- 问题又来了,如果我们有一个玩具鸭子ToyDuck, 这样就需要ToyDuck去覆盖Duck的所有实现的方法
策略模式
策略模式类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qDBHC6G3-1611137918441)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120162629683.png)]
代码实现
//FlyBehavior策略接口
public interface FlyBehavior {
void fly();
}
//FlyBehavior策略具体类GoodFlyBehavior
public class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("此鸭子飞得很牛");
}
}
//FlyBehavior策略具体类NoFlyBehavior
public class NoFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("此鸭不会飞");
}
}
/FlyBehavior策略具体类BadFlyBehavior
public class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("此鸭子飞得一般啊");
}
}
//QuackBehavior策略接口
public interface QuackBehavior {
void quack();
}
//QuackBehavior策略具体类GoodQuackBehavior
public class GoodQuackBehavior implements QuackBehavior {
@Override
public void quack() {
System.out.println("此鸭子叫:呀呀呀");
}
}
//QuackBehavior策略具体类NoQuackBehavior
public class NoQuackBehavior implements QuackBehavior {
@Override
public void quack() {
System.out.println("此鸭子不会叫啊");
}
}
//duck抽象类,维护了策略类
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public abstract void display();
public void swim(){
System.out.println("鸭子会游泳");
}
public void quack(){
if(quackBehavior!=null){
quackBehavior.quack();
}
}
public void fly(){
if(flyBehavior!=null){
flyBehavior.fly();
}
}
}
//duck的具体类WildDuck
public class WildDuck extends Duck {
public WildDuck(){
flyBehavior=new GoodFlyBehavior();
quackBehavior=new GoodQuackBehavior();
}
@Override
public void display() {
System.out.println("野生鸭子");
}
}
//duck的具体类ToyDuck
public class ToyDuck extends Duck {
public ToyDuck(){
flyBehavior=new NoFlyBehavior();
quackBehavior=new NoQuackBehavior();
}
@Override
public void display() {
System.out.println("玩具鸭鸭鸭");
}
}
//duck的具体类PekingDuck
public class PekingDuck extends Duck {
public PekingDuck(){
flyBehavior=new BadFlyBehavior();
quackBehavior=new NoQuackBehavior();
}
@Override
public void display() {
System.out.println("北京鸭");
}
}
//client测试
public class Client {
public static void main(String[] args) {
ToyDuck toyDuck=new ToyDuck();
toyDuck.quack();
toyDuck.fly();
toyDuck.swim();
PekingDuck pekingDuck=new PekingDuck();
pekingDuck.quack();
pekingDuck.fly();
}
}
优缺点
- 策略模式的关键是:分析项目中变化部分与不变部分
- 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
- 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else if…else)
- 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
- 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大
策略模式在JDK-Arrays应用的源码分析
JDK的 Arrays 的Comparator就使用了策略模式
职责链模式
基本介绍
- 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦。
- 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
- 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y0SaOsDp-1611137918443)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120174925419.png)]
原理类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WrNRaz62-1611137918443)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120175010204.png)]
原理类图说明
- Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外Handler
- ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
- Request,含义很多属性,表示一个请求
案例理解
编写程序完成学校OA系统的采购审批项目
- 采购员采购教学器材
- 如果金额 小于等于5000, 由教学主任审批
- 如果金额 小于等于10000, 由院长审批
- 如果金额 小于等于30000, 由副校长审批
- 如果金额 超过30000以上,有校长审批
案例类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JibPXrw6-1611137918444)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210120175251716.png)]
代码实现
//抽象的责任链Approver
public abstract class Approver {
Approver approver;
String name;
public Approver(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
//下一个处理者
public void setApprover(Approver approver) {
this.approver = approver;
}
//处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
//具体的责任链DepartmentApprover
public class DepartmentApprover extends Approver{
public DepartmentApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() <= 5000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
//具体的责任链CollegeApprover
public class CollegeApprover extends Approver{
public CollegeApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
//具体的责任链ViceSchoolMasterApprover
public class ViceSchoolMasterApprover extends Approver {
public ViceSchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
//具体的责任链SchoolMasterApprover
public class SchoolMasterApprover extends Approver {
public SchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() > 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
//请求对象
public class PurchaseRequest {
private int type = 0; //请求类型
private float price = 0.0f; //请求金额
private int id = 0;
//构造器
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
public int getId() {
return id;
}
}
//client调用
public class Client {
public static void main(String[] args) {
PurchaseRequest purchaseRequest=new PurchaseRequest(1,3000,1);
DepartmentApprover departmentApprover=new DepartmentApprover("黄老师");
CollegeApprover collegeApprover=new CollegeApprover("李主任");
ViceSchoolMasterApprover viceSchoolMasterApprover=new ViceSchoolMasterApprover("陈副校长");
SchoolMasterApprover schoolMasterApprover=new SchoolMasterApprover("冯校长");
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
schoolMasterApprover.setApprover(departmentApprover);//形成闭环
departmentApprover.processRequest(purchaseRequest);
}
}
优缺点
- 将请求和处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
- 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
- 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器
参考资料地址说明
参考资料来源:www.atguigu.com
[参考资料来源][www.atguigu.com]
chaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
//具体的责任链SchoolMasterApprover
public class SchoolMasterApprover extends Approver {
public SchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() > 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
//请求对象
public class PurchaseRequest {
private int type = 0; //请求类型
private float price = 0.0f; //请求金额
private int id = 0;
//构造器
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
public int getId() {
return id;
}
}
//client调用
public class Client {
public static void main(String[] args) {
PurchaseRequest purchaseRequest=new PurchaseRequest(1,3000,1);
DepartmentApprover departmentApprover=new DepartmentApprover(“黄老师”);
CollegeApprover collegeApprover=new CollegeApprover(“李主任”);
ViceSchoolMasterApprover viceSchoolMasterApprover=new ViceSchoolMasterApprover(“陈副校长”);
SchoolMasterApprover schoolMasterApprover=new SchoolMasterApprover(“冯校长”);
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
schoolMasterApprover.setApprover(departmentApprover);//形成闭环
departmentApprover.processRequest(purchaseRequest);
}
}
### 优缺点
- 将请求和处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
- 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
- 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器
---
## 参考资料地址说明
参考资料来源:www.atguigu.com
[参考资料来源][www.atguigu.com]
如若侵权,请联系删除