Java设计模式 - 状态模式
😄 不断学习才是王道
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 一个有梦有戏的人 @怒放吧德德
🌝分享学习心得,欢迎指正,大家一起学习成长!
简介
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
状态模式原理解析
在不同的状态会有自己不同的操作方法(行为),状态和行为是一一对应的,并且状态之间可以互相切换。
UML图:
角色与职责
- Context:环境角色,聚合State实例,用来定义当前状态。
- State:抽象类-状态类,定义一些行为。
- ConcreteState:具体的状态类,实现实现具体的行为。
状态模式实例
本次实验采用抽奖的案例来解析状态模式。
抽奖的流程是先扣除积分,抽奖,如果中奖则发放奖品。使用状态模式,需要定义不可抽奖、可以抽奖、发放奖品、奖品发送完成状态。在每个状态中都有各自的详细行为。
类图如图:
①、定义抽象状态类
里面包含扣除积分、是否中奖以及发放奖品,分别由各自状态去实现对应的方法。
package com.lyd.demo.state;
/**
* @Author: lyd
* @Description: 状态抽象类
* @Date: 2022-09-12
*/
public abstract class State {
// 扣除积分
public abstract void deductMoney();
// 是否抽中奖品
public abstract boolean raffle();
// 发放奖品
public abstract void dispensePrize();
}
②、定义具体状态
不可抽奖状态
初始状态,实现扣除积分,这个状态会扣除积分,这个状态还不能抽奖,调用抽奖和发放奖品都会提示不可以抽奖。
package com.lyd.demo.state;
import com.lyd.demo.activity.RaffleActivity;
/**
* @Author: lyd
* @Description: 子类 - 禁止抽奖
* @Date: 2022-09-12
*/
public class NoRaffleState extends State{
RaffleActivity activity;
public NoRaffleState(RaffleActivity activity) {
this.activity = activity;
}
/**
* 扣除积分
*/
@Override
public void deductMoney() {
// 抽奖的时候需要扣除积分
System.out.println("扣除20积分,可以抽奖......");
// 转换成可以抽奖的状态
activity.setState(activity.getCanRaffleState());
}
/**
* 是否可以抽奖
* @return
*/
@Override
public boolean raffle() {
System.out.println("当前状态不能抽奖......");
return false;
}
/**
* 发放奖品 - 当前状态不能发放
*/
@Override
public void dispensePrize() {
System.out.println("当前状态不能发放奖品......");
}
}
可以抽奖状态
具体实现抽奖行为,并且禁止扣除积分,和发放奖品,在抽奖获奖的时候会去将状态转换成发放奖品状态,反之则转换成不可以抽奖状态。
package com.lyd.demo.state;
import com.lyd.demo.activity.RaffleActivity;
import java.util.Random;
/**
* @Author: lyd
* @Description: 可以抽奖状态
* @Date: 2022-09-12
*/
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.getNoRaffleState());
return false;
}
}
@Override
public void dispensePrize() {
System.out.println("没中奖,不能发放奖品......");
}
}
发放奖品状态
如果抽奖获奖了,就转换成该状态,在此状态依然是不允许扣除积分,以及抽奖也是不允许的,只能够实现发放奖品。发放奖品还要将状态转成不可抽奖状态,如果是奖品发放完了就切换成奖品发放完的状态。
package com.lyd.demo.state;
import com.lyd.demo.activity.RaffleActivity;
/**
* @Author: lyd
* @Description: 发放奖品状态
* @Date: 2022-09-12
*/
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.getNoRaffleState());
}else{
System.out.println("很遗憾,奖品发送完了");
// 改变状态为奖品发送完毕, 后面我们就不可以抽奖
activity.setState(activity.getDispenseOutState());
System.out.println("抽奖活动结束!!!");
System.exit(0);
}
}
}
奖品发放完状态
package com.lyd.demo.state;
import com.lyd.demo.activity.RaffleActivity;
/**
* @Author: lyd
* @Description: 奖品发放完的状态
* @Date: 2022-09-12
*/
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("奖品发送完了,请下次再参加");
}
}
③、定义活动类
对像的实例化,刚开始的状态是不可以抽奖的状态。
package com.lyd.demo.activity;
import com.lyd.demo.state.*;
/**
* @Author: lyd
* @Description: 活动
* @Date: 2022-09-12
*/
public class RaffleActivity {
// 状态变化
State state = null;
// 奖品数量
int count = 0;
// 四种状态
// 禁止抽奖状态
State noRaffleState = new NoRaffleState(this);
// 允许抽奖状态
State canRaffleState = new CanRaffleState(this);
// 发放奖品状态
State dispenseState = new DispenseState(this);
// 奖品发放结束状态
State dispenseOutState = new DispenseOutState(this);
// 构造方法,初始化数据,一开始是不允许抽奖状态
public RaffleActivity(int count) {
this.state = getNoRaffleState();
this.count = count;
}
//扣分, 调用当前状态的 deductMoney
public void deductMoney() {
this.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 getNoRaffleState() {
return noRaffleState;
}
public void setNoRaffleState(State noRaffleState) {
this.noRaffleState = noRaffleState;
}
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 getDispenseOutState() {
return dispenseOutState;
}
public void setDispenseOutState(State dispenseOutState) {
this.dispenseOutState = dispenseOutState;
}
}
④、测试
实例化活动,通过调用扣分和抽奖方法。
package com.lyd.demo.test;
import com.lyd.demo.activity.RaffleActivity;
/**
* @Author: lyd
* @Description: 测试
* @Date: 2022-09-12
*/
public class StateTest {
public static void main(String[] args) {
// 创建活动,输入奖品个数
RaffleActivity activity = new RaffleActivity(2);
for (int i = 0; i < 5; i++) {
System.out.println("--------第" + (i + 1) + "次抽奖----------");
// 参加抽奖,第一步点击扣除积分
activity.deductMoney();
// 第二步抽奖
activity.raffle();
}
}
}
运行结果
👍创作不易,可能有些语言不是很通畅,如有错误请指正,感谢观看!记得一键三连哦!👍
💓德德小建议:
理解设计模式不是一件简单的事情,需要不断的学习和动手去练习,才能理解。只有掌握好设计模式,才能够真正的理解SpringAOP和Mybatis的底层原理。各位读者可以和我一样,动手敲一敲代码,甚至用不同的例子来做,通过debug一步一步调试,还有就是多看看别人的例子。能够有助于理解!谢谢各位观看指点!❤️ ❤️ ❤️