愿你如阳光,明媚不忧伤。
目録
6. 责任链(职责)模式
Chain of Responsibility 为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
责任链(职责)模式优点和缺点
- 优点
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
- 缺点
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
责任链(职责)模式的应用场景
- 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
责任链(职责)模式模式的结构
责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
责任链(职责)模式的实现
package com.example.demo.controller;
public class ChainController {
public static void main(String[] args) {
// 组装责任链
Leader teacher1 = new ClassAdviser();
Leader teacher2 = new DepartmentHead();
Leader teacher3 = new Dean();
Leader teacher4 = new DeanOfStudies();
teacher1.setNext(teacher2);
teacher2.setNext(teacher3);
teacher3.setNext(teacher4);
// 提交请求
teacher1.handleRequest(14);
}
}
// 抽象处理者:领导类
abstract class Leader {
private Leader next;
public void setNext(Leader next) {
this.next = next;
}
public Leader getNext() {
return next;
}
// 处理请求的方法
public abstract void handleRequest(int LeaveDays);
}
// 具体处理者1:班主任类
class ClassAdviser extends Leader {
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 2) {
System.out.println("班主任批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
// 具体处理者2:系主任类
class DepartmentHead extends Leader {
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 7) {
System.out.println("系主任批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
// 具体处理者3:院长类
class Dean extends Leader {
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 10) {
System.out.println("院长批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
// 具体处理者4:教务处长类
class DeanOfStudies extends Leader {
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 20) {
System.out.println("教务处长批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
-----------------------------------------------------------------
・【运行结果】
教务处长批准您请假14天。
7. 状态模式
State 当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,用各个不同的类进行表示,系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了 if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展力。
状态模式优点和缺点
- 优点
- 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
- 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
- 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
- 缺点
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
状态模式的应用场景
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
状态模式的结构
状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。1. 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
2. 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
3. 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
状态模式的实现
package com.example.demo.controller;
public class StateController {
public static void main(String[] args) {
// 创建环境
StateContext stateContext = new StateContext();
// 处理请求
stateContext.Handle();
stateContext.Handle();
stateContext.Handle();
stateContext.Handle();
}
}
// 环境类
class StateContext {
private State state;
// 定义环境类的初始状态
public StateContext() {
this.state = new ConcreteStateA();
}
// 设置新状态
public void setState(State state) {
this.state = state;
}
// 读取状态
public State getState() {
return (state);
}
// 对请求做处理
public void Handle() {
state.Handle(this);
}
}
//抽象状态类
abstract class State {
public abstract void Handle(StateContext stateContext);
}
// 具体状态A类
class ConcreteStateA extends State {
public void Handle(StateContext stateContext) {
System.out.println("当前状态是 A.");
stateContext.setState(new ConcreteStateB());
}
}
// 具体状态B类
class ConcreteStateB extends State {
public void Handle(StateContext stateContext) {
System.out.println("当前状态是 B.");
stateContext.setState(new ConcreteStateA());
}
}
-----------------------------------------------------------------
・【运行结果】
当前状态是 A.
当前状态是 B.
当前状态是 A.
当前状态是 B.
状态模式的应用实例 I
用“状态模式”设计一个学生成绩的状态转换程序。
package com.example.demo.controller;
public class StateController {
public static void main(String[] args) {
ScoreContext account = new ScoreContext();
System.out.println("学生成绩状态测试:");
account.add(30);
account.add(40);
account.add(25);
account.add(-15);
account.add(-25);
}
}
// 环境类
class ScoreContext {
private AbstractState state;
ScoreContext() {
state = new LowState(this);
}
public void setState(AbstractState state) {
this.state = state;
}
public AbstractState getState() {
return state;
}
public void add(int score) {
state.addScore(score);
}
}
// 抽象状态类
abstract class AbstractState {
// 环境
protected ScoreContext scoreContext;
// 状态名
protected String stateName;
// 分数
protected int score;
// 检查当前状态
public abstract void checkState();
public void addScore(int score) {
this.score += score;
System.out.print("加上:" + score + "分, 当前分数:" + score);
checkState();
System.out.println("分, 当前状态:" + scoreContext.getState().stateName);
}
}
//具体状态类:不及格
class LowState extends AbstractState {
public LowState(ScoreContext scoreContext) {
this.scoreContext = scoreContext;
stateName = "不及格";
score = 0;
}
public LowState(AbstractState state) {
scoreContext = state.scoreContext;
stateName = "不及格";
score = state.score;
}
public void checkState() {
if (score >= 90) {
scoreContext.setState(new HighState(this));
} else if (score >= 60) {
scoreContext.setState(new MiddleState(this));
}
}
}
//具体状态类:中等
class MiddleState extends AbstractState {
public MiddleState(AbstractState state) {
scoreContext = state.scoreContext;
stateName = "中等";
score = state.score;
}
public void checkState() {
if (score < 60) {
scoreContext.setState(new LowState(this));
} else if (score >= 90) {
scoreContext.setState(new HighState(this));
}
}
}
//具体状态类:优秀
class HighState extends AbstractState {
public HighState(AbstractState state) {
scoreContext = state.scoreContext;
stateName = "优秀";
score = state.score;
}
public void checkState() {
if (score < 60) {
scoreContext.setState(new LowState(this));
} else if (score < 90) {
scoreContext.setState(new MiddleState(this));
}
}
}
-----------------------------------------------------------------
・【运行结果】
学生成绩状态测试:
加上:30分, 当前分数:30分, 当前状态:不及格
加上:40分, 当前分数:40分, 当前状态:中等
加上:25分, 当前分数:25分, 当前状态:优秀
加上:-15分, 当前分数:-15分, 当前状态:中等
加上:-25分, 当前分数:-25分, 当前状态:不及格
状态模式的应用实例 II
用“状态模式”设计一个多线程的状态转换程序。
package com.example.demo.controller;
public class StateInstanceController {
public static void main(String[] args) {
ThreadContext context = new ThreadContext();
context.start();
context.getCPU();
context.suspend();
context.resume();
context.getCPU();
context.stop();
}
}
//环境类
class ThreadContext {
private ThreadState state;
ThreadContext() {
state = new New();
}
public void setState(ThreadState state) {
this.state = state;
}
public ThreadState getState() {
return state;
}
public void start() {
((New) state).start(this);
}
public void getCPU() {
((Runnable) state).getCPU(this);
}
public void suspend() {
((Running) state).suspend(this);
}
public void stop() {
((Running) state).stop(this);
}
public void resume() {
((Blocked) state).resume(this);
}
}
//抽象状态类:线程状态
abstract class ThreadState {
protected String stateName; //状态名
}
//具体状态类:新建状态
class New extends ThreadState {
public New() {
stateName = "新建状态";
System.out.println("当前线程处于:新建状态.");
}
public void start(ThreadContext hj) {
System.out.print("调用start()方法-->");
if (stateName.equals("新建状态")) {
hj.setState(new Runnable());
} else {
System.out.println("当前线程不是新建状态,不能调用start()方法.");
}
}
}
//具体状态类:就绪状态
class Runnable extends ThreadState {
public Runnable() {
stateName = "就绪状态";
System.out.println("当前线程处于:就绪状态.");
}
public void getCPU(ThreadContext hj) {
System.out.print("获得CPU时间-->");
if (stateName.equals("就绪状态")) {
hj.setState(new Running());
} else {
System.out.println("当前线程不是就绪状态,不能获取CPU.");
}
}
}
//具体状态类:运行状态
class Running extends ThreadState {
public Running() {
stateName = "运行状态";
System.out.println("当前线程处于:运行状态.");
}
public void suspend(ThreadContext hj) {
System.out.print("调用suspend()方法-->");
if (stateName.equals("运行状态")) {
hj.setState(new Blocked());
} else {
System.out.println("当前线程不是运行状态,不能调用suspend()方法.");
}
}
public void stop(ThreadContext hj) {
System.out.print("调用stop()方法-->");
if (stateName.equals("运行状态")) {
hj.setState(new Dead());
} else {
System.out.println("当前线程不是运行状态,不能调用stop()方法.");
}
}
}
//具体状态类:阻塞状态
class Blocked extends ThreadState {
public Blocked() {
stateName = "阻塞状态";
System.out.println("当前线程处于:阻塞状态.");
}
public void resume(ThreadContext hj) {
System.out.print("调用resume()方法-->");
if (stateName.equals("阻塞状态")) {
hj.setState(new Runnable());
} else {
System.out.println("当前线程不是阻塞状态,不能调用resume()方法.");
}
}
}
//具体状态类:死亡状态
class Dead extends ThreadState {
public Dead() {
stateName = "死亡状态";
System.out.println("当前线程处于:死亡状态.");
}
}
-----------------------------------------------------------------
・【运行结果】
当前线程处于:新建状态.
调用start()方法-->当前线程处于:就绪状态.
获得CPU时间-->当前线程处于:运行状态.
调用suspend()方法-->当前线程处于:阻塞状态.
调用resume()方法-->当前线程处于:就绪状态.
获得CPU时间-->当前线程处于:运行状态.
调用stop()方法-->当前线程处于:死亡状态.
状态模式的扩展
在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享。
- 享元状态模式
package com.example.demo.controller;
import java.util.HashMap;
public class ShareStateController {
public static void main(String[] args) {
ShareContext shareContext = new ShareContext();
shareContext.handle();
shareContext.handle();
shareContext.handle();
shareContext.handle();
}
}
// 享元环境类
class ShareContext {
private ShareState shareState;
private HashMap<String, ShareState> shareStateHashMap = new HashMap<>();
// 初始化享元状态
public ShareContext() {
shareState = new ConcreteShareState1();
shareStateHashMap.put("c1", shareState);
shareState = new ConcreteShareState2();
shareStateHashMap.put("c2", shareState);
shareState = getShareState("c1");
}
// 获取享元状态
public ShareState getShareState(String key) {
return shareStateHashMap.get(key);
}
// 设置新状态
public void setShareState(ShareState shareState) {
this.shareState = shareState;
}
// 对请求做处理
public void handle() {
shareState.handle(this);
}
}
// 抽象享元状态类
abstract class ShareState {
public abstract void handle(ShareContext shareContext);
}
// 具体享元状态类1
class ConcreteShareState1 extends ShareState {
@Override
public void handle(ShareContext shareContext) {
System.out.println("当前状态为:具体享元状态1");
shareContext.setShareState(shareContext.getShareState("c2"));
}
}
// 具体享元状态类2
class ConcreteShareState2 extends ShareState {
@Override
public void handle(ShareContext shareContext) {
System.out.println("当前状态为:具体享元状态2");
shareContext.setShareState(shareContext.getShareState("c1"));
}
}
-----------------------------------------------------------------
・【运行结果】
当前状态为:具体享元状态1
当前状态为:具体享元状态2
当前状态为:具体享元状态1
当前状态为:具体享元状态2
【每日一面】
说说状态模式和责任链模式的区别
状态模式和责任链模式都能消除 if-else 分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用。
State:状态模式强调的是一个对象内在状态的改变,状态模式的各个状态对象知道自己要进入的下一个状态对象,各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户无法指定状态,只能设置初始状态。
Chain of Responsibility:责任链模式强调的是外部节点对象间的改变,责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责。
都是解决分支过多的问题。状态模式强调对象内部状态的改变,知道下一步的操作,而责任链模式强调外部节点间的改变,不知道下一步的操作