Java设计模式(十)—状态模式、策略模式、职责链模式

第24章 状态模式

24.1 应用场景:APP抽奖

请编写程序完成 APP 抽奖活动 具体要求如下:
(1)假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10%
(2)奖品数量固定,抽完就不能抽奖
(3)活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完 4) 活动的四个状态转换关系图
### 三级目录

24.2 状态模式

  1. 基本介绍
    (1)状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
    (2)当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
  2. 原理类图
    在这里插入图片描述
    状态模式的角色及职责)
    (1)Context 类为环境角色, 用于维护 State 实例,这个实例定义当前状态
    (2)State 是抽象状态角色,定义一个接口封装与 Context 的一个特点接口相关行为
    (3)ConcreteState 具体的状态角色,每个子类实现一个与 Context 的一个状态相关行为。

24.3 应用案例:APP抽奖

  1. 类图
    (1)定义出一个接口叫状态接口,每个状态都实现它。
    (2)接口有扣除积分方法、抽奖方法、发放奖品方法

在这里插入图片描述
说明
(1)状态类:NoRaffleState(不可抽奖)、CanRaffleState(可以抽奖)、DispensePrizeState(领奖状态)、DispenseOutPrizeState(奖品领完状态)。
2. 代码实现
(1)状态抽象类

/**
 * 状态抽象类
 */
public abstract class State {
	// 扣除积分 - 50
    public abstract void deductMoney();
    // 是否抽中奖品
    public abstract boolean raffle();
    // 发放奖品
    public abstract  void dispensePrize();
}

(2)具体状态栏
不能抽奖状态

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("不能发放奖品");
    }
}

可以抽奖状态

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("没中奖,不能发放奖品");
    }
}

发放奖品状态

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);
        }
    }
}

奖品发放完毕状态

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("奖品发送完了,请下次再参加");
    }
}

(3)抽奖活动

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;
    }
    //State、NoRafflleState、CanRaffleState、DispenseState、DispensOutState的set和get方法
  }

24.4 状态模式的注意事项

(1)代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类
(2)方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都
要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
(3)符合开闭原则。容易增删状态
(4)会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
(5)应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式

第25章 策略模式

25.1 应用场景:鸭子项目

(1)有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等)
(2)显示鸭子的信息

25.2 传统方案

  1. 传统方案
    在这里插入图片描述
    写一个Duck类,鸭子继承Duck类,如果没有新的内容,就继承;如果有新的内容就添加,
  2. 传统方案分析
    (1)其它鸭子,都继承了 Duck 类,所以 fly 让所有子类都会飞了,这是不正确的
    (2)上面说的 1 的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。会有溢出效应
    (3)为了改进 1 问题,我们可以通过覆盖 fly 方法来解决 => 覆盖解决
    (4)问题又来了,如果我们有一个玩具鸭子 ToyDuck, 这样就需要 ToyDuck 去覆盖 Duck 的所有实现的方法
  3. 解 决思路 -》 策略模式 (strategy pattern)

25.3 策略模式

  1. 基本介绍
    (1)策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
    (2)这算法体现了几个设计原则,
    第一、把变化的代码从不变的代码中分离出来
    第二、针对接口编程而不是具体类(定义了策略接口);
    第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
  2. 类图
    在这里插入图片描述
    说明:
    从上图可以看到,客户 context 有成员变量 strategy 或者其他的策略接口
    ,至于需要使用到哪个策略,我们可以在构造器中指定

25.4 应用案例:鸭子项目

  1. 策略模式
    策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是: 分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者
  2. 类图
    在这里插入图片描述
  3. 代码实现
    (1)飞翔接口
public interface FlyBehavior {
	void fly(); // 子类具体实现
}

(2)具体飞翔实现类

public class GoodFlyBehavior implements FlyBehavior {
	@Override
	public void fly() {
		System.out.println(" 飞翔技术高超 ~~~");
	}
}

NoFlyBehavior、BadFlyBehavior类似。
(3)聚合各类行为的Duck类

public abstract class Duck {

	//属性, 策略接口
	FlyBehavior flyBehavior;
	//其它属性<->策略接口
	QuackBehavior quackBehavior;
	
	public Duck() {
	}
	public abstract void display();//显示鸭子信息
	public void quack() {
		System.out.println("鸭子嘎嘎叫~~");
	}
	public void swim() {
		System.out.println("鸭子会游泳~~");
	}
	public void fly() {
		//改进
		if(flyBehavior != null) {
			flyBehavior.fly();
		}
	}
	public void setFlyBehavior(FlyBehavior flyBehavior) {
		this.flyBehavior = flyBehavior;
	}
	public void setQuackBehavior(QuackBehavior quackBehavior) {
		this.quackBehavior = quackBehavior;
	}
}

(4)鸭子实现类

public class PekingDuck extends Duck {
	//假如北京鸭可以飞翔,但是飞翔技术一般
	public PekingDuck() {
		flyBehavior = new BadFlyBehavior();
	}
	@Override
	public void display() {
		System.out.println("~~北京鸭~~~");
	}
}

(5)客户端

public class Client {
	public static void main(String[] args) {
		PekingDuck pekingDuck = new PekingDuck();
		pekingDuck.fly();
		
		//动态改变某个对象的行为, 北京鸭 不能飞
		pekingDuck.setFlyBehavior(new NoFlyBehavior());
		System.out.println("北京鸭的实际飞翔能力");
		pekingDuck.fly();
	}
}

25,5 策略模式的注意事项

(1)策略模式的关键是:分析项目中变化部分与不变部分
(2)策略模式的核心思想是:多用组合/聚合 少用继承用行为类组合,而不是行为的继承。更有弹性
(3)体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else if…else)
(4)提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
(5)需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大

第26章 职责链模式

26.1 应用场景:学校 OA 系统的采购审批项目

需求是:采购员采购教学器材:
(1)如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000)
(2)如果金额 小于等于 10000, 由院长审批 (5000<x<=10000)
(3)如果金额 小于等于 30000, 由副校长审批 (10000<x<=30000)
(4)如果金额 超过 30000 以上,有校长审批 ( 30000<x)

26.2 传统方案

  1. 传统方案
    在这里插入图片描述
  2. 传统方案问题分析
    (1)传统方式是:接收到一个采购请求后,根据采购金额来调用对应的 Approver (审批人)完成审批。
    (2)传统方式的问题分析 : 客户端这里会使用到 分支判断(比如 switch) 来对不同的采购请求处理, 这样就存在如下问题 (1) 如果各个级别的人员审批金额发生变化,在客户端的也需要变化 (2) 客户端必须明确的知道 有多少个审批级别和访问
    (3)这样 对一个采购请求进行处理 和 Approver (审批人) 就存在强耦合关系,不利于代码的扩展和维护
  3. 解决方案:职责链模式

26.3 职责链模式(责任链模式)

  1. 基本介绍
    (1)职责链模式(Chain of Responsibility Pattern),为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦
    (2)职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
    (3)这种类型的设计模式属于行为型模式

在这里插入图片描述
2. 类图
在这里插入图片描述
职责链模式的角色
(1)Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
(2)ConcreteHandlerA , B 是具体的处理者,处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
(3)Request , 含义很多属性,表示一个请求

26.4 OA系统采购审批

  1. 类图
    在这里插入图片描述
  2. 代码实现
    (1)请求类
//请求类
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;
	}
}

(2)抽象处理类

public abstract class Approver {
	Approver approver;  //下一个处理者
	String name; // 名字
	public Approver(String name) {
		this.name = name;
	}
	//下一个处理者
	public void setApprover(Approver approver) {
		this.approver = approver;
	}
	//处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
	public abstract void processRequest(PurchaseRequest purchaseRequest);
}

(3)具体实现类

public class DepartmentApprover extends Approver {
	public DepartmentApprover(String name) {
		super(name);
	}
	@Override
	public void processRequest(PurchaseRequest purchaseRequest) {
		if(purchaseRequest.getPrice() <= 5000) {
			System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
		}else {
			approver.processRequest(purchaseRequest);
		}
	}
}

CollegeApprover、SchoolMasterApprover、ViceSchoolMasterApprover类类似。
(4)客户端

public class Client {
	public static void main(String[] args) {
		//创建一个请求
		PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 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);
		viceSchoolMasterApprover.processRequest(purchaseRequest);
	}
}

26.5 职责链模式的注意事项

(1)将请求和处理分开,实现解耦,提高系统的灵活性
(2)简化了对象,使对象不需要知道链的结构
(3)性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能。
(4)调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
(5)最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat对 Encoding 的处理、拦截器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东风难破

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值