文章目录
- 前言
- 一、状态模式
- 二、策略模式
- 三、责任链模式
前言
本篇是关于设计模式中的状态模式、策略模式、以及责任链模式的学习笔记。
一、状态模式
状态模式是一种行为设计模式
,核心思想在于,使某个对象在其内部状态改变时,改变该对象的行为,使对象看起来像是改变了其类。将状态的相关行为封装到独立的状态类中,并通过在运行时切换状态对象来改变对象的行为。实现方式在于,将状态抽取成单独的类,并由上下文类统一管理。
状态模式的结构包括以下几个主要角色:
- 上下文类:它维护一个当前状态的实例,并且对客户端公开其接口。
- 抽象状态类:定义了所有具体状态类需要实现的接口,抽取出所有状态下共有的方法。
- 具体状态类:实现了抽象状态类,根据自身状态的特点,有选择性第重写父类方法的逻辑。
假设需要设计一个抽奖系统,有四种状态:
- 不能抽奖的状态
- 可以抽奖的状态
- 发放奖品的状态
- 奖品发放完成的状态
状态流转方式如下:
体现在代码上,首先设计一个抽象状态接口
,该接口将各种状态下可能的操作进行聚合:
/**
* 状态接口
* 将各种状态下可能的操作聚合成一个接口
* 实现类根据各自状态可能的业务去重写其中的方法
*/
public interface State {
/**
* 扣除积分
*/
void deduceMoney();
/**
* 抽奖
* @return
*/
boolean raffle();
/**
* 发放奖品
*/
void dispensePrice();
}
抽象接口具体的实现
:
不能抽奖状态
,在此状态下只能去做出扣减积分的操作,扣减完成积分后,状态会变更为可抽奖状态
。
/**
* 不能抽奖的状态
*/
public class NoRaffleState implements State{
RaffleActivity raffleActivity;
public NoRaffleState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
/**
* 扣除积分
*/
@Override
public void deduceMoney() {
System.out.println("扣除50积分,可以抽奖");
raffleActivity.setState(raffleActivity.getCanRaffleState());
}
/**
* 抽奖
*
* @return
*/
@Override
public boolean raffle() {
System.out.println("扣除积分方能抽奖");
return false;
}
/**
* 发放奖品
*/
@Override
public void dispensePrice() {
System.out.println("不能发放奖品");
}
}
可抽奖状态
,只能进行抽奖操作:
- 未抽中奖品,重新回到
不可抽奖状态
。 - 抽中了奖品,流转到
发送奖品状态
。
/**
* 可以抽奖的状态
*/
public class CanRaffleState implements State{
RaffleActivity raffleActivity;
public CanRaffleState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
/**
* 扣除积分
*/
@Override
public void deduceMoney() {
System.out.println("已经扣除积分,可以抽奖");
}
/**
* 抽奖
*
* @return
*/
@Override
public boolean raffle() {
System.out.println("正在抽奖,请稍等");
int num = new Random().nextInt(10);
if (num == 0){
//中奖了,发放奖品
raffleActivity.setState(raffleActivity.getDispenseState());
return true;
}else {
System.out.println("很遗憾,您没有中奖");
//回到不能抽奖的状态
raffleActivity.setState(raffleActivity.getNoRaffleState());
return false;
}
}
/**
* 发放奖品
*/
@Override
public void dispensePrice() {
System.out.println("没中奖,不能发放奖品");
}
}
发放奖品状态
,只能进行发放奖品的操作:
- 奖品数量充足,流转到
奖品发送完成
状态。 - 奖品数量不足,改变状态为
不能抽奖
。
/**
* 发放奖品的状态
*/
public class DispenseState implements State{
RaffleActivity raffleActivity;
public DispenseState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
/**
* 扣除积分
*/
@Override
public void deduceMoney() {
System.out.println("不能扣除积分");
}
/**
* 抽奖
*
* @return
*/
@Override
public boolean raffle() {
System.out.println("不能抽奖");
return false;
}
/**
* 发放奖品
*/
@Override
public void dispensePrice() {
if (raffleActivity.getCount()>0){
System.out.println("恭喜中奖了");
//改变状态为不能抽奖
raffleActivity.setState(raffleActivity.getNoRaffleState());
}else {
System.out.println("很遗憾,奖品发送完了");
//改变状态为奖品发送完成
raffleActivity.setState(raffleActivity.getDispenseOutState());
}
}
}
奖品发放完成
,则不可以进行任何操作
/**
* 奖品发放完成的状态
*/
public class DispenseOutState implements State{
RaffleActivity raffleActivity;
public DispenseOutState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
/**
* 扣除积分
*/
@Override
public void deduceMoney() {
System.out.println("奖品发送完了,请下次再参加");
}
/**
* 抽奖
*
* @return
*/
@Override
public boolean raffle() {
System.out.println("奖品发送完了,请下次再参加");
return false;
}
/**
* 发放奖品
*/
@Override
public void dispensePrice() {
System.out.println("奖品发送完了,请下次再参加");
}
}
创建一个上下文类
,对状态进行统一管理,同时对状态进行初始化,并且上面的具体状态类,也聚合了上下文类
的对象:
/**
* 抽奖的行为
*/
public class RaffleActivity {
/**
* 初始的状态
*/
State state = null;
/**
* 初始的数量
*/
int count = 0;
State canRaffleState = new CanRaffleState(this);
State noRaffleState = new NoRaffleState(this);
State dispenseOutState = new DispenseOutState(this);
State dispenseState = new DispenseState(this);
public RaffleActivity(int count) {
//初始状态,不能抽奖
this.state = getNoRaffleState();
this.count = count;
}
/**
* 扣除积分
*/
public void deduceMoney(){
state.deduceMoney();
}
/**
* 抽奖
*/
public void raffle(){
if (state.raffle()){
//领取奖品
state.dispensePrice();
}
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public int getCount() {
int curCount = this.count;
count--;
return curCount;
}
public void setCount(int count) {
this.count = count;
}
public State getCanRaffleState() {
return canRaffleState;
}
public void setCanRaffleState(State canRaffleState) {
this.canRaffleState = canRaffleState;
}
public State getNoRaffleState() {
return noRaffleState;
}
public void setNoRaffleState(State noRaffleState) {
this.noRaffleState = noRaffleState;
}
public State getDispenseOutState() {
return dispenseOutState;
}
public void setDispenseOutState(State dispenseOutState) {
this.dispenseOutState = dispenseOutState;
}
public State getDispenseState() {
return dispenseState;
}
public void setDispenseState(State dispenseState) {
this.dispenseState = dispenseState;
}
}
public class Client {
public static void main(String[] args) {
RaffleActivity raffleActivity = new RaffleActivity(1);
for (int i = 0; i < 3; i++) {
//扣积分
raffleActivity.deduceMoney();
//抽奖
raffleActivity.raffle();
}
}
}
小结
状态模式的优缺点
优点:
- 遵循单一职责原则:将与状态相关的代码封装到独立类中。
- 开闭原则:添加新状态非常方便,无需修改现有代码。
- 消除大量条件语句:状态切换通过对象替换实现,而非复杂的条件判断。
缺点:
- 类的数量增加:每个状态都需要创建一个类,可能导致类数量增多。
- 状态之间的耦合:状态类之间可能需要频繁切换,会导致一定程度的耦合。
二、策略模式
策略模式是一种行为设计模式
,核心思想在于,定义了一组算法,将每个算法封装到独立的类中,使它们可以相互替换。
策略模式通常包括以下角色:
- 上下文类:持有一个策略对象的引用,并且初始化具体策略。
- 抽象策略类:定义了所有具体策略类需要实现的接口。
- 具体策略类:实现抽象策略类,包含具体的算法或行为。
假设目前有一个方法,需要根据参数中传入的不同的业务类型,执行不同业务的分派,如果使用传统方式,则会利用switch或者if-else实现,将来需要扩展的时候,需要对分支代码进行修改:
/**
* 业务处理接口
*/
public interface BusinessClass {
/**
* 处理业务
*/
void handleBusiness(int type);
}
public class BusinessClassImpl implements BusinessClass{
/**
* 处理业务
* 根据传入的参数进行分发
*/
@Override
public void handleBusiness(int type) {
switch (type){
case 1:
this.handleBusiness1();
break;
case 2:
this.handleBusiness2();
break;
case 3:
this.handleBusiness3();
break;
default:
break;
}
}
private void handleBusiness3() {
System.out.println("业务3的具体逻辑");
}
private void handleBusiness2() {
System.out.println("业务2的具体逻辑");
}
private void handleBusiness1() {
System.out.println("业务1的具体逻辑");
}
}
使用策略模式改造,则可以抽取一个策略接口
:
/**
* 业务策略接口
*/
public interface BusinessStrategy {
/**
* 处理具体的业务逻辑
*/
void handleBusiness();
}
将传统方式中,每个分支中的方法都抽取到一个单独的类中,实现策略接口
public class Business1 implements BusinessStrategy{
/**
* 处理具体的业务逻辑
*/
@Override
public void handleBusiness() {
System.out.println("处理业务逻辑1");
}
}
public class Business2 implements BusinessStrategy{
/**
* 处理具体的业务逻辑
*/
@Override
public void handleBusiness() {
System.out.println("处理业务逻辑2");
}
}
public class Business3 implements BusinessStrategy{
/**
* 处理具体的业务逻辑
*/
@Override
public void handleBusiness() {
System.out.println("处理业务逻辑3");
}
}
在上下文类
中,对其进行统一的管理,业务代码只需要注入上下文类
,调用其中的方法即可。
public class Context {
private Map<Integer, BusinessStrategy> strategyMap = new HashMap<>();
/**
* 初始化上下文,k业务类型 v 业务类型具体的逻辑
*/
public Context(){
strategyMap.put(1, new Business1());
strategyMap.put(2, new Business2());
strategyMap.put(3, new Business3());
}
/**
* 处理业务
* 根据传入的参数进行分发
*/
public void handleBusiness(int type) {
BusinessStrategy strategy = strategyMap.get(type);
if (strategy != null) {
strategy.handleBusiness();
} else {
System.out.println("无效的业务类型");
}
}
}
小结
策略模式的优缺点
优点:
- 遵循开闭原则:添加新策略无需修改现有代码,只需扩展新策略类。
- 消除条件语句:避免了复杂的 if-else 或 switch-case 语句。
- 提高灵活性:客户端可以动态选择策略。
缺点:
- 类数量增加:每种策略需要创建一个类,可能导致类的数量过多。
- 客户端需要了解不同策略:客户端必须知道有哪些策略才能选择合适的策略。
三、责任链模式
责任链模式是一种行为设计模式
,核心目的在于,将请求的处理责任沿着一条链传递,直到有对象处理这个请求为止。责任链模式将请求的发送者和接收者解耦,且可以动态地调整链条上的处理顺序。
责任链模式通常包含以下角色:
- 抽象处理者:定义了处理请求的接口,以及一个指向下一个处理者的引用。(即将自身作为属性存放在类中)
- 具体处理者:继承了抽象处理者,具体去实现各自处理请求的逻辑,以及无法处理时/处理完成时,应该调用的下一个处理者
- 客户端:创建并组装责任链,通常会将请求发送给责任链的首个处理者。
下面来看一个案例:假设某个学校,购买教材,器材,根据金额不同,需要审批的级别也不同。
首先构建一个请求对象
:
public class Request {
/**
* 请求id
*/
protected int id = 0;
/**
* 价格
*/
protected int price = 0;
public Request() {
}
public Request(int id, int price) {
this.id = id;
this.price = price;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
然后定义一个抽象处理者
:
/**
* 抽象审批人
*/
public abstract class Approver {
/**
* 审批人名称
*/
protected String name;
/**
* 持有自己的引用
*/
protected Approver approver;
public Approver() {
}
public Approver(String name) {
this.name = name;
}
public void setApprover(Approver approver) {
this.approver = approver;
}
/**
* 子类具体去实现处理请求的逻辑
* @param request
*/
abstract void handleReq(Request request);
}
定义具体处理人
,继承抽象处理人,编写自己处理的判断逻辑:
public class TeachingDirector extends Approver{
public TeachingDirector(String name){
super(name);
}
/**
* 子类具体去实现处理请求的逻辑
*
* @param request
*/
@Override
void handleReq(Request request) {
if (request.getPrice()<= 5000){
System.out.println("请求编号"+ request.id + "已经被" + name + "处理");
}else {
approver.handleReq(request);
}
}
}
public class Dean extends Approver{
public Dean(String name){
super(name);
}
/**
* 子类具体去实现处理请求的逻辑
*
* @param request
*/
@Override
void handleReq(Request request) {
if (request.getPrice()<= 10000){
System.out.println("请求编号"+ request.id + "已经被" + name + "处理");
}else {
approver.handleReq(request);
}
}
}
public class Principal extends Approver{
public Principal(String name){
super(name);
}
/**
* 子类具体去实现处理请求的逻辑
*
* @param request
*/
@Override
void handleReq(Request request) {
if (request.getPrice()<= 20000){
System.out.println("请求编号"+ request.id + "已经被" + name + "处理");
}else {
approver.handleReq(request);
}
}
}
最后由客户端
对其进行统一组装:
public class Client {
public static void main(String[] args) {
Request request = new Request(1, 8000);
Dean dean = new Dean("院长");
TeachingDirector teachingDirector = new TeachingDirector("教学主任");
Principal principal = new Principal("校长");
//设置下一个审批者
teachingDirector.setApprover(dean);
dean.setApprover(principal);
principal.setApprover(teachingDirector);
//8000 教学主任处理不了,交给下一级(院长)处理
teachingDirector.handleReq(request);
}
}
小结
责任链模式的优缺点
优点:
- 降低耦合度:请求发送者与接收者解耦,无需关心由谁处理请求。
- 动态灵活:可以在运行时动态调整责任链的结构。
- 增强扩展性:新增处理者无需修改现有代码。
缺点:
- 链条可能过长:请求可能需要经过多个处理者,性能可能受影响。
- 调试困难:由于请求的处理流程不明确,可能导致调试复杂。