目录
一、模版方法模式(template )
概念
模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
比如定义一个操作中的算法的骨架,将步骤延迟到子类中。模板方法使得子类能够不去改变一个算法的结构即可重定义算法的某些特定步骤。
模式中的角色
1)AbstractClass 抽象类实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法ConcreteClass
2)实现抽象方法 , 以完成算法中特点子类的步骤
模板模式UML类图
案例
需求:统计某一代码运行时间
使用前
package com.javaxl.design.template.before;
/**
* @author 周大福ye
* @site www.javaxl.com
* @company
* @create 2020-02-24 15:42
*/
public class CodeTotalTime {
public static void template(){
long start = System.currentTimeMillis();
// 检测Operation_1方法运行的时长======33
Operation_1();
// 检测Operation_2方法运行的时长======616
// Operation_2();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public static void Operation_1(){
for (int i = 0; i<1000 ;i++){
System.out.println("模拟耗时操作...");
}
System.out.print("检测Operation_1方法运行的时长======");
}
public static void Operation_2(){
for (int i = 0; i<20000 ;i++){
System.out.println("模拟耗时操作...");
}
System.out.print("检测Operation_2方法运行的时长======");
}
}
public class Client {
public static void main(String[] args) {
CodeTotalTime.template();
}
}
使用后
abstract class CodeAbstractClass {
public void template() {
long start = System.currentTimeMillis();
method();
long end = System.currentTimeMillis();
System.out.println("当前方法执行时长:" + (end - start));
}
public abstract void method();
}
class ConcreteClassA extends CodeAbstractClass {
@Override
public void method() {
for (int i = 0; i < 1000; i++) {
System.out.println("模拟耗时操作...");
}
System.out.print("检测ConcreteClassA.method方法运行的时长======");
}
}
class ConcreteClassB extends CodeAbstractClass {
@Override
public void method() {
for (int i = 0; i < 20000; i++) {
System.out.println("模拟耗时操作...");
}
System.out.print("ConcreteClassB.method方法运行的时长======");
}
}
public class Client {
public static void main(String[] args) {
//检测ConcreteClassA.method方法运行的时长======当前方法执行时长:
new ConcreteClassA().template();
//ConcreteClassB.method方法运行的时长======当前方法执行时长:
new ConcreteClassB().template();
}
}
钩子函数应用场景
public abstract class CodeAbstractClass {
public void template() {
long start = System.currentTimeMillis();
if (callback()) method();
long end = System.currentTimeMillis();
System.out.println("当前方法执行时长:" + (end - start));
}
public abstract void method();
public boolean callback() {
return true;
}
}
从上面可以看出:template方法默认是用作统计method方法的执行时长,但是有的时候我们无需统计代码时长,template函数中有一些其它逻辑要执行,在这里我们可以考虑采用钩子函数;钩子函数被子类覆写,覆写成false,那么method方法就不会被调用,不再统计代码时长了;前端框架Vue的生命周期就有多处用到钩子函数;
注意事项和细节
- 钩子函数在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”
- 算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
- 一般模板方法都加上 final 关键字, 防止子类重写模板方法
应用
Spring IOC容器加载
优点
模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
子类实现算法的某些细节,有助于算法的扩展。
通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
模板方法模式与开闭原则
什么是“开闭原则”?
开闭原则是指一个软件实体应该对扩展开放,对修改关闭。也就是说软件实体必须是在不被修改的情况下被扩展。模板方法模式意图是由抽象父类控制顶级逻辑,并把基本操作的实现推迟到子类去实现,这是通过继承的手段来达到对象的复用,同时也遵守了开闭原则。
父类通过顶级逻辑,它通过定义并提供一个具体方法来实现,我们也称之为模板方法。通常这个模板方法才是外部对象最关心的方法。所以它必须是public的,才能被外部对象所调用。同时,因为子类不能覆写一个被定义为final的方法。从而保证了子类的逻辑永远由父类所控制。
二、命令模式
概念:
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化
术语
Command:命令
ConcreteCommand:具体的命令
Invoker:调用者
Receiver:接受者
标准UML类图
角色
Command抽象命令 执行命令 撤销命令
ConcreteCommandLightOnCommand 开灯 LightOffCommand 关灯
NonCommand
空命令
Invoker调用者 遥控器聚合所有命令 Command[] ons Command[] offs Command undo
Receiver接受者 电灯、空调、电视
案例
需求:万能遥控器的制作
使用前
class AirConditioner {
public void on() {
System.out.println("空调打开...");
}
public void off() {
System.out.println("空调关闭...");
}
}
class Television {
public void on() {
System.out.println("电视打开...");
}
public void off() {
System.out.println("电视关闭...");
}
}
public class Invoker {
private Light light = new Light();
private AirConditioner airConditioner = new AirConditioner();
private Television television = new Television();
public void lightOn() {
light.on();
}
public void lightOff() {
light.off();
}
public void airOn() {
airConditioner.on();
}
public void airOff() {
airConditioner.off();
}
public void tvOn() {
television.on();
}
public void tvOff() {
television.off();
}
}
public class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker();
invoker.lightOn();
invoker.lightOff();
System.out.println("=============");
invoker.airOn();
invoker.airOff();
System.out.println("=============");
invoker.tvOn();
invoker.tvOff();
}
}
使用后
public class Light {
public void on() {
System.out.println("电灯打开...");
}
public void off() {
System.out.println("电灯关闭...");
}
}
class AirConditioner {
public void on() {
System.out.println("空调打开...");
}
public void off() {
System.out.println("空调关闭...");
}
}
class Television {
public void on() {
System.out.println("电视打开...");
}
public void off() {
System.out.println("电视关闭...");
}
}
interface Command {
void execute();
void undo();
}// 空命令
class NonCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
class LightOnCommand implements Command {
private Light light = new Light();
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
class LightOffCommand implements Command {
private Light light = new Light();
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
class TvOnCommand implements Command {
private Television tv = new Television();
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
}
class TvOffCommand implements Command {
private Television tv = new Television();
@Override
public void execute() {
tv.off();
}
@Override
public void undo() {
tv.on();
}
}
public class Invoker {
Command[] ons;
Command[] offs;// 记录上一个命令
Command command;
public Invoker(int n) {
ons = new Command[n];
offs = new Command[n];
command = new NonCommand();
for (int i = 0; i < n; i++) {
setCommand(i, new NonCommand(), new NonCommand());
}
}
public void setCommand(int no, Command on, Command off) {
ons[no] = on;
offs[no] = off;
}
public Command getOnCommand(int no) {
return ons[no];
}
public Command getOffCommand(int no) {
return offs[no];
}
// 执行命令
public void invoke(Command command) {
// 执行当前命令
command.execute();// 保存当前执行命令
this.command = command;
}// 撤销命令(上个操作的反操作)
public void undo() {// 这里就能体现定义一个空命令的好处了,如果第一次按撤销命令,那么应该什么都不做;// 如果没有定义空命令的话,此时就需要判断空处理了
command.undo();
}
}
public class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker(2);
invoker.setCommand(0, new LightOnCommand(), new LightOffCommand());
invoker.setCommand(1, new TvOnCommand(), new TvOffCommand());
System.out.println("电灯打开关闭操作===========");
invoker.invoke(invoker.getOnCommand(0));
invoker.invoke(invoker.getOffCommand(0));
// invoker.undo();
System.out.println("电视打开关闭操作===========");
invoker.invoke(invoker.getOnCommand(1));
invoker.undo();
}
}
注意事项和细节:
将发起请求的对象与执行请求的对象解耦 容易实现对请求的撤销和重做 空命令也是一种设计模式,它为我们省去了判空的操作
命令模式不足: 可能导致某些系统有过多的具体命令类,增加了系统的复杂度
与外观模式相似:都是将多个功能聚合在一起 外观模式更多适用于维护;命令模式更多应用于设计
总结
命令模式是一种使用频率非常高的设计模式,它可以将请求发送者与接收者解耦,请求发送者通过命令对象来间接引用请求接收者,使得系统具有更好的灵活性和可扩展性。
主要优点
(1) 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
(2) 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。
(3) 可以比较容易地设计一个命令队列或宏命令(组合命令)。
(4) 为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案。
主要缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。
适用场景
系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。
系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
系统需要将一组操作组合在一起形成宏命令。
应用:Spring框架中的JdbcTemplate类 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令 界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
三、备忘录模式
概念
备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。
术语
Memento:备忘录
originator:发起者
Caretaker:守护者
模式结构
在备忘录模式结构图中包含如下几个角色
- Originator(原发器):它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
- Memento(备忘录):存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。
- Caretaker(负责人):负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节
案例
需求:游戏人物大战后状态恢复
情况一:为一个对象保留一个状态
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
}
// 恢复上一个英雄状态
public void getMemento(HeroMemento heroMemento) {
this.state = heroMemento.getState();
}
}
public class Caretaker {
private HeroMemento heroMemento;
public HeroMemento getHeroMemento() {
return heroMemento;
}
public void setHeroMemento(HeroMemento heroMemento) {
this.heroMemento = heroMemento;
}
}
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态2:状态下滑");
System.out.println("当前的状态===============" + hero.getState());
hero.getMemento(caretaker.getHeroMemento());
System.out.println("当前的状态===============" + hero.getState());
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态3:残血状态");
hero.getMemento(caretaker.getHeroMemento());
System.out.println("当前的状态===============" + hero.getState());
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态4:临死状态");
caretaker.setHeroMemento(hero.saveHero());
}
}
情况二:为一个对象保留多个状态
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
} // 恢复某一个英雄状态
public void getMemento(Caretaker caretaker, int no) {
this.state = caretaker.getMemento(no).getState();
}
}
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class Caretaker {
private List<HeroMemento> heroMementos = new ArrayList<>();
public void addMemento(HeroMemento memento) {
heroMementos.add(memento);
}
public HeroMemento getMemento(int no) {
return heroMementos.get(no);
}
}
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
caretaker.addMemento(hero.saveHero());
hero.setState("状态2:状态下滑");
hero.setState("状态3:残血状态");
caretaker.addMemento(hero.saveHero());
hero.setState("状态4:临死状态");
caretaker.addMemento(hero.saveHero());
hero.setState("状态5:死亡状态");
// 上面备份了1、3、4状态,我来恢复看看
System.out.println("当前的状态===============" + hero.getState());
hero.getMemento(caretaker, 0);
System.out.println("回复到状态1===============" + hero.getState());
hero.getMemento(caretaker, 1);
System.out.println("回复到状态3===============" + hero.getState());
hero.getMemento(caretaker, 2);
System.out.println("回复到状态4===============" + hero.getState());
}
}
情况三:为多个对象保留一个状态
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
} // 恢复某一个英雄状态
}
public class Caretaker {
private HashMap<Caretaker ,HeroMemento> mementos = new HashMap();
public void addMemento(Caretaker caretaker,HeroMemento memento) {
mementos.put(caretaker,memento);
}
public HashMap<Caretaker ,HeroMemento> getMemento() {
return this.mementos;
}
}
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
Caretaker caretaker2 = new Caretaker();
HeroMemento heroMemento = new HeroMemento(hero.getState());
caretaker.addMemento(caretaker,heroMemento);
caretaker.addMemento(caretaker2,heroMemento);
HashMap<Caretaker, HeroMemento> memento = caretaker.getMemento();
Set<Map.Entry<Caretaker, HeroMemento>> entrySet = memento.entrySet();
for (Map.Entry<Caretaker, HeroMemento> entry : entrySet) {
String key = entry.getKey().toString();
String value = entry.getValue().toString();
System.out.println("key=" + key + " value=" + value);
}
}
情况四:为多个对象保留多个对象
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
} // 恢复某一个英雄状态
}
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class Caretaker {
private HashMap<Caretaker , List<HeroMemento>> mementos = new HashMap();
public void addMemento(Caretaker caretaker,List<HeroMemento> memento) {
mementos.put(caretaker,memento);
}
public HashMap<Caretaker , List<HeroMemento>> getMemento() {
return this.mementos;
}
}
public class Client {
public static void main(String[] args) {
Hero hero1 = new Hero("状态1,满血状态");
Hero hero2 = new Hero("状态2,满血状态");
Hero hero3 = new Hero("状态3,满血状态");
Hero hero4 = new Hero("状态4,满血状态");
Caretaker caretaker = new Caretaker();
Caretaker caretaker2 = new Caretaker();
HeroMemento heroMemento1 = new HeroMemento(hero1.getState());
HeroMemento heroMemento2 = new HeroMemento(hero2.getState());
HeroMemento heroMemento3 = new HeroMemento(hero3.getState());
HeroMemento heroMemento4 = new HeroMemento(hero4.getState());
List<HeroMemento> lsit1=new ArrayList<>();
lsit1.add(heroMemento1);
lsit1.add(heroMemento2);
List<HeroMemento> lsit2=new ArrayList<>();
lsit2.add(heroMemento3);
lsit2.add(heroMemento4);
caretaker.addMemento(caretaker,lsit1);
caretaker.addMemento(caretaker2,lsit2);
HashMap<Caretaker, List<HeroMemento>> memento = caretaker.getMemento();
Set<Map.Entry<Caretaker, List<HeroMemento>>> entries = memento.entrySet();
for (Map.Entry<Caretaker, List<HeroMemento>> entry : entries) {
String key = entry.getKey().toString();
String value = entry.getValue().toString();
System.out.println("key=" + key + " value=" + value);
}
}
注意事项和细节:
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
实现了信息的封装,使得用户不需要关心状态的保存细节
如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
总结
备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销操作。如果需要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。在一些字处理软件、图像编辑软件、数据库管理系统等软件中备忘录模式都得到了很好的应用。
主要优点
它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
主要缺点
资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
适用场景
保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
应用
- 事务回滚
- 游戏的存档
- Selenium动态解析资源树
四、状态模式
概念
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
术语
State:状态
角色
Context:环境角色:用于维护 State 实例,这个实例定义当前状态State:抽象状态角色:聚合到Context环境角色中ConcreteState :具体的状态角色 ConcreteStateA ConcreteStateB ConcreteStateC
模式结构
案例:
抽奖活动项目设计
出现前
package com.javaxl.design.state.before;
import java.util.Random;
/**
* @author 周大福ye
* @create 2020-02-25 12:57
*/
public class State {
// 当前的状态
private int state;
// 供抽奖的积分
private int score;
// 奖品的数量
private int count;
public State(int score, int count) {
this.score = score;
this.count = count;
}
public int getCount() {
return count;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
// 扣除积分
public void minus() {
// 只有一阶段可以扣积分
this.state = 1;
if (this.state == 1) {
if (this.score >= 50) {
if (this.count == 0) {
System.out.println("奖品领完....");
return;
}
this.score = this.score - 50;
System.out.println("========扣除50积分,当前积分还剩" + this.score + "========");
this.state = 2;
if (luckHit()) {
this.state = 3;
getPrize();
}
} else {
System.out.println("========积分不够,当前积分为" + this.score + "========");
}
}
}
// 十分之一抽中奖品的概率
public boolean luckHit() {
// 只有二阶段可以抽奖
return this.state == 2 ? (new Random().nextInt(10) == 6) : false;
}
public void getPrize() {
if (this.state == 3) {
if (this.count > 0) {
System.out.println("领取奖品....");
this.count = this.count - 1;
} else {
System.out.println("奖品领完....");
}
}
}
}
public class Client {
public static void main(String[] args) {
State state = new State(500,1);
// state.minus();
for (int i = 0; i < 300; i++) {
state.minus();
}
}
}
从上面的编码中,我们可以看出,完成该需求有很多的条件判断,非常不利于后续的维护;上面状态只有4个,代码已经比较复杂了;状态越多,代码嵌套就越复杂,维护成本就越高;
出现后
package com.javaxl.design.state.after;
import java.util.Random;
/**
* @author 周大福ye
* @create 2020-02-25 15:59
*/
public abstract class State {
// 扣积分
abstract void minus();
// 抽奖
abstract boolean luckHit();
// 获取奖品
abstract void getPrize();
}
class ConcreteStateA extends State{
Context context;
public ConcreteStateA(Context context) {
this.context = context;
}
@Override
void minus() {
if(context.getScore()>=50){
context.setScore(context.getScore()-50);
System.out.println("========扣除50积分,当前积分还剩"+context.getScore()+"========");
context.setState(context.getStateB());
}else{
System.out.println("========积分不够,当前积分为"+context.getScore()+"========");
}
}
@Override
boolean luckHit() {
System.out.println("还在扣费环节,不能抽奖...");
return false;
}
@Override
void getPrize() {
System.out.println("还在扣费环节,不能领取奖品...");
}
}
class ConcreteStateB extends State{
Context context;
public ConcreteStateB(Context context) {
this.context = context;
}
@Override
void minus() {
System.out.println("已经在抽奖环节...");
}
@Override
boolean luckHit() {
boolean flag = new Random().nextInt(10) == 6;
if(flag){
context.setState(context.getStateC());
}else{
context.setState(context.getStateA());
}
return flag;
}
@Override
void getPrize() {
System.out.println("还在抽奖环节,不能领取奖品...");
}
}
class ConcreteStateC extends State{
Context context;
public ConcreteStateC(Context context) {
this.context = context;
}
@Override
void minus() {
System.out.println("已经在领取奖品环节...");
}
@Override
boolean luckHit() {
System.out.println("已经在领取奖品环节...");
return false;
}
@Override
void getPrize() {
if(context.getCount()>0){
System.out.println("领取奖品成功...");
context.setState(context.getStateA());
}else {
System.out.println("活动结束,领取奖品失败...");
context.setState(context.getStateD());
// 不继续抽奖
// System.exit(0);
}
}
}
class ConcreteStateD extends State{
Context context;
public ConcreteStateD(Context context) {
this.context = context;
}
@Override
void minus() {
System.out.println("已经在活动结束,奖品送完环节...");
}
@Override
boolean luckHit() {
System.out.println("已经在活动结束,奖品送完环节...");
return false;
}
@Override
void getPrize() {
System.out.println("已经在活动结束,奖品送完环节...");
}
}
public class Context {
// 当前的状态
private State state;
// 奖品数量
public int count;
// 用户积分
private int score;
// 表示同一个对象的四种状态
private ConcreteStateA stateA = new ConcreteStateA(this);
private ConcreteStateB stateB = new ConcreteStateB(this);
private ConcreteStateC stateC = new ConcreteStateC(this);
private ConcreteStateD stateD = new ConcreteStateD(this);
public Context(int score, int count) {
this.score = score;
this.count = count;
this.state = stateA;
}
// 扣积分
public void minus() {
state.minus();
}
// 抽奖
public void luckHit() {
if (state.luckHit()) {
state.getPrize();
}
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public int getCount() {
return count--;
}
public void setCount(int count) {
this.count = count;
}
public ConcreteStateA getStateA() {
return stateA;
}
public void setStateA(ConcreteStateA stateA) {
this.stateA = stateA;
}
public ConcreteStateB getStateB() {
return stateB;
}
public void setStateB(ConcreteStateB stateB) {
this.stateB = stateB;
}
public ConcreteStateC getStateC() {
return stateC;
}
public void setStateC(ConcreteStateC stateC) {
this.stateC = stateC;
}
public ConcreteStateD getStateD() {
return stateD;
}
public void setStateD(ConcreteStateD stateD) {
this.stateD = stateD;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
public class Client {
public static void main(String[] args) {
// 这次游戏积分500个,用完为止,总奖品数2
Context context = new Context(500,1);
// context.lunkHit();//还在扣费环节,不能抽奖...
for (int i = 0; i < 300; i++) {
context.minus();
context.luckHit();
}
System.out.println("------------------");
}
}
一次没抽中
抽中一次
抽中两次
总结
注意事项和细节
代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
方便维护。将容易产生问题的 if-else 语句删除了
符合“开闭原则”。容易增删状态
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点:会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式
应用
借贷平台状态管理
五、责任链模式
概念
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
术语
Chain of Responsibility:责任链
角色
Handler 抽象的处理者, 定义了一个处理请求的接口ConcreteHandlerA , B 具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者)Request 含义很多属性,表示一个请求
案例
需求:OA系统请假审批案例
学生请假1天:教员审批
学生请假2天:教学主管审批
学生请假3天:教学经理审批
学生请假5天:副校长审批
学生请假超过5天:校长审批
使用前:
package com.javaxl.design.chain.before;
/**
* @author 周大福ye
* @site www.javaxl.com
* @company
* @create 2020-02-25 19:30
*/
public class Request {
private String content;
private int day;
public Request(String content, int day) {
this.content = content;
this.day = day;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
package com.javaxl.design.chain.before;
/**
* @author 周大福ye
* @site www.javaxl.com
* @company
* @create 2020-02-25 19:31
*/
public class Handler {
public void handle(Request request){
int day = request.getDay();
if(day <= 1){
System.out.println("教员处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else if(day <= 2){
System.out.println("教学主管处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else if(day <= 3){
System.out.println("教学经理处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else if(day <= 5){
System.out.println("副校长处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else {
System.out.println("校长处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}
}
}
public class Client { public static void main(String[] args) { Handler handler = new Handler(); Request request1 = new Request("小感冒",1); handler.handle(request1); Request request2 = new Request("做检查",2); handler.handle(request2); Request request3 = new Request("打点滴",3); handler.handle(request3); Request request4 = new Request("住院",4); handler.handle(request4); Request request5 = new Request("在家调养",30); handler.handle(request5); }}
违背了迪米特法则,调用方清楚的知道整个处理链的存在;
使用后
public class Request {
private String content;
private int day;
public Request(String content, int day) {
this.content = content;
this.day = day;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
public abstract class Handler {
Handler next;
String name;
public Handler(String name) {
this.name = name;
}
public Handler getNext() {
return next;
}
public void setNext(Handler next) {
this.next = next;
}
public abstract void handle(Request request);
}
class HandlerA extends Handler {
public HandlerA(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 1) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerB extends Handler {
public HandlerB(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 2) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerC extends Handler {
public HandlerC(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 3) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerD extends Handler {
public HandlerD(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 5) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerE extends Handler {
public HandlerE(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}
}
public class Client {
public static void main(String[] args) {
HandlerA handlerA = new HandlerA("教员");
HandlerB handlerB = new HandlerB("教学主管");
HandlerC handlerC = new HandlerC("教学经理");
HandlerD handlerD = new HandlerD("副校长");
HandlerE handlerE = new HandlerE("校长");
handlerA.setNext(handlerB);
handlerB.setNext(handlerC);
handlerC.setNext(handlerD);
handlerD.setNext(handlerE);
Request request1 = new Request("小感冒",1);
handlerA.handle(request1);
Request request2 = new Request("做检查",2);
handlerA.handle(request2);
Request request3 = new Request("打点滴",3);
handlerA.handle(request3);
Request request4 = new Request("住院",4);
handlerA.handle(request4);
Request request5 = new Request("在家调养",30);
handlerA.handle(request5);
}
}
总结
注意事项和细节
将请求和处理分开,实现解耦,提高系统的灵活性简化了对象,使对象不需要知道链的结构
注意:性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能。
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
应用
springmvc框架~HandlerExcutionChain类
工作流框架绘图生成xml反射实例化,整个流程归档过程
js中的原型链
六、观察者模式
概念
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
术语
Observer:观察者
Subject:主题
角色
Observer观察者 百度 新浪 谷歌
Subject 气象局 WeatherData
模式结构
案例
需求:气象站数据更新推送问题
使用前
package com.javaxl.design.observer.before;
/**
* @author 周大福ye
* @site www.javaxl.com
* @company
* @create 2020-02-26 18:08
*/
public class WeatherData {
double temperature;
double humidity;
public WeatherData(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
public void getWeatherInfo() {
System.out.println("当前温度:" + temperature + ",当前湿度:" + humidity);
}
public void change(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
}
public class Baidu {
private WeatherData weatherData;
public Baidu(WeatherData weatherData) {
this.weatherData = weatherData;
}
public void getWeatherInfo() {
System.out.print("百度网站温馨提示===>");
weatherData.getWeatherInfo();
}
}
class Sina {
private WeatherData weatherData;
public Sina(WeatherData weatherData) {
this.weatherData = weatherData;
}
public void getWeatherInfo() {
System.out.print("新浪网站温馨提示===>");
weatherData.getWeatherInfo();
}
}
public class Client {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData(30,20);
Baidu baidu = new Baidu(weatherData);
Sina sina = new Sina(weatherData);
baidu.getWeatherInfo();
sina.getWeatherInfo();
weatherData.change(10,10);
baidu.getWeatherInfo();
sina.getWeatherInfo();
}
}
由第三方(百度、新浪)主动获取最新天气信息,这种方案需要每个第三方主动定时获取最新天气数据,涉及多个第三方;
使用后
package com.javaxl.design.observer.after;
import java.util.ArrayList;
import java.util.List;
/**
* @author 周大福ye
* @site www.javaxl.com
* @company
* @create 2020-02-26 19:14
*/
public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
class WeatherData implements Subject{
double temperature;
double humidity;
List<Observer> Observers = new ArrayList<>();
public WeatherData(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
public void update(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
// 气象局数据一改变,马上通知接入的第三方/观察者
notifyObservers();
}
@Override
public void addObserver(Observer observer) {
Observers.add(observer);
observer.update(this.temperature,this.humidity);
}
@Override
public void removeObserver(Observer observer) {
Observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : Observers) {
observer.update(this.temperature,this.humidity);
}
}
}
public interface Observer {
void display();
void update(double temperature, double humidity);
}
class Baidu implements Observer{
double temperature;
double humidity;
@Override
public void display() {
System.out.println("百度温馨提示:当前温度:" + temperature + ",当前湿度:" + humidity);
}
@Override
public void update(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
this.display();
}
}
class Sina implements Observer{
double temperature;
double humidity;
@Override
public void display() {
System.out.println("新浪温馨提示:当前温度:" + temperature + ",当前湿度:" + humidity);
}
@Override
public void update(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
this.display();
}
}
public class Client {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData(30, 20);
Baidu baidu = new Baidu();
Sina sina = new Sina();
weatherData.addObserver(baidu);
weatherData.addObserver(sina);
weatherData.update(10, 10);
weatherData.removeObserver(baidu);
weatherData.update(12, 12);
}
}
由气象局主动通知第三方,天气数据发生了改变;并且,第三方的接入可以控制(增加、删除、通知);
总结
注意事项和细节
集合的方式来管理用户(Observer),包括注册,移除和通知
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
应用
JDK源码中Observable类
七、策略模式
概念
定义: 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
模式结构
Context
环境
Strategy
策略接口 ConcreteStrategyA ConcreteStrategyB
Strategy2
策略接口 ConcreteStrategyC ConcreteStrategyD
案例
需求:学院共有专业需求
出现前
package com.javaxl.design.strategy.before;
/**
* @author钟羽
* @site www.javaxl.com
* @company
*/
public class Major {
private String name;
public Major(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
Major major = (Major) obj;
return this.name.equals(major.name);
}
}
public class College {
String name;
public College(String name) {
this.name = name;
}
}
class CollegeA extends College{
List<Major> list = new ArrayList<>();
CollegeA(String name){
super(name);
this.list.add(new Major("JAVA"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("android"));
}
}
class CollegeB extends College{
List<Major> list = new ArrayList<>();
CollegeB(String name){
super(name);
this.list.add(new Major("iOS"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("嵌入式"));
}
}
public class StrategyA {
public List<Major> intersect(List<Major> a,List<Major> b){
List<Major> list = new ArrayList();
for (Major major : a) {
if(b.contains(major)){
list.add(major);
}
}
return list;
}
}
class StrategyB {
public List<Major> intersect(List<Major> a,List<Major> b){
// a.retainAll(b);
b.retainAll(a);
return b;
}
}
public class Client {
public static void main(String[] args) {
StrategyA strategyA = new StrategyA();
CollegeA a = new CollegeA("华东交通大学");
CollegeB b = new CollegeB("东华理工大学");
List<Major> intersect = strategyA.intersect(a.list, b.list);
System.out.println(a.name + "与" + b.name + "都有的专业");
for (Major major : intersect) {
System.out.println(major.getName());
}
StrategyB strategyB = new StrategyB();
List<Major> intersect2 = strategyB.intersect(a.list, b.list);
System.out.println(a.name + "与" + b.name + "都有的专业");
for (Major major : intersect2) {
System.out.println(major.getName());
}
}
}
出现后
package com.javaxl.design.strategy.after;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class Major {
private String name;
public Major(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
Major major = (Major) obj;
return this.name.equals(major.name);
}
}
public class College {
String name;
public College(String name) {
this.name = name;
}
}
class CollegeA extends College {
List<Major> list = new ArrayList<>();
CollegeA(String name){
super(name);
this.list.add(new Major("JAVA"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("android"));
}
}
class CollegeB extends College {
List<Major> list = new ArrayList<>();
CollegeB(String name){
super(name);
this.list.add(new Major("iOS"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("嵌入式"));
}
}
public interface Strategy {
List<Major> intersect(List<Major> a, List<Major> b);
}
public class StrategyA implements Strategy{
public List<Major> intersect(List<Major> a,List<Major> b){
List<Major> list = new ArrayList();
for (Major major : a) {
if(b.contains(major)){
list.add(major);
}
}
return list;
}
}
public class Context {
public List<Major> intersect(List<Major> a, List<Major> b,Strategy strategy){
return strategy.intersect(a,b);
}
}
public class Client {
public static void main(String[] args) {
CollegeA a = new CollegeA("华东交通大学");
CollegeB b = new CollegeB("东华理工大学");
Context context = new Context();
List<Major> intersect = context.intersect(a.list, b.list, new StrategyA());
System.out.println(a.name + "与" + b.name + "都有的专业");
for (Major major : intersect) {
System.out.println(major.getName());
}
// 可以随意定制策略
List<Major> intersect2 = context.intersect(a.list, b.list, new Strategy() {
@Override
public List<Major> intersect(List<Major> a, List<Major> b) {
a.retainAll(b);
return a;
}
});
System.out.println(a.name + "与" + b.name + "都有的专业========");
for (Major major : intersect2) {
System.out.println(major.getName());
}
}
}
总结
注意事项和细节
分析项目中变化部分与不变部分
体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可
策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
注意:在很多场景中,策略接口会作为内部接口体现
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
应用
Arrays工具类的排序方法Comparator策略接口的使用
JDBC对Result结果集的处理
作者能力有限,难免疏漏,望谅解!