Java设计模式-状态模式(Builder Pattern)
目录
- 什么是状态模式
- 状态模式的实现
- JavaSE中状态模式的使用
- Struts2状态模式的应用
它主要用来解决对象在多种状态转换时,需要对外 输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
一、什么状态模式
状态模式的定义为:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
看下状态模式的UML类图
环境(Context)角色:也称上下文,通常用来定义客户端感兴趣的接口,同时维护一个来具体处理当前状态的对象示例。
抽象状态(State)角色:定义一个接口,用来封装与环境(Context)对象的一个特定的状态所对应的行为。
具体状态(ConcreteState)角色:每一个具体状态类都实现了一个跟环境(Context)相关的状态的具体处理。
状态模式只看定义比较难理解,我们来说下他的核心思想:
- 各个状态是单独的类,状态直接可以相互切换(通过状态类中的方法进行切换)
- 环境角色中持有所有的状态类,并且环境角色类维护一个当前状态,这个当前状态就是各个状态类来动态改变的,也就是说各个状态类中也持有环境角色对象
所以状态模式的重点就是:状态的切换、状态的维护
二、状态模式的实现
我们通过一个抽奖活动的案例讲解下状态模式,先看下UML类图与核心类介绍
环境(Context)角色:RaffleActivity
抽象状态(State)角色:State
具体状态(ConcreteState)角色:CanRaffleState、DispenseOutState、DispenseState、NoRaffleState
大家都玩过抽奖活动,比如超市中可以使用积分兑换抽奖活动,当所有的奖品都发放后抽奖活动就结束了。对于这个场景而已,环境角色就是抽奖活动,而是否可以抽奖就是状态。简单分析下就能得到上图中的类图关系。
RaffleActivity持有四个活动状态,每个活动状态中都有RaffleActivity对象,为的就是切换状态,比如有人中奖了,就要将RaffleActivity的状态变为DispenseState
接下来看下代码实现:
package org.state.version1;
import java.util.Random;
abstract class State{
public RaffleActivity raffleActivity;
public State(RaffleActivity raffleActivity){
this.raffleActivity = raffleActivity;
}
// 扣除积分
public abstract void deductMoney();
// 抽奖动作
public abstract boolean raffle();
// 发放奖品
public abstract void dispensePrize();
}
/**
* 可以抽奖的状态类
*/
class CanRaffleState extends State{
public CanRaffleState(RaffleActivity raffleActivity) {
super(raffleActivity);
}
@Override
public void deductMoney() {
System.out.println("正在抽奖,无需扣除积分");
}
@Override
public boolean raffle() {
System.out.println("正在抽奖");
Random random = new Random();
if (random.nextInt(10) == 1){
System.out.println("恭喜您中奖了");
// 修改活动状态为发放奖品状态
super.raffleActivity.setState(super.raffleActivity.dispenseState);
return true;
}else {
System.out.println("很遗憾没有中奖");
super.raffleActivity.setState(super.raffleActivity.noRaffleState);
return false;
}
}
@Override
public void dispensePrize() {
System.out.println("还没抽到奖,不能发奖品");
}
}
/**
* 奖品发放的状态类
*/
class DispenseState extends State{
public DispenseState(RaffleActivity raffleActivity) {
super(raffleActivity);
}
@Override
public void deductMoney() {
System.out.println("已经抽到奖了,不需要扣除积分");
}
@Override
public boolean raffle() {
System.out.println("发放奖品不需要扣除积分");
return false;
}
@Override
public void dispensePrize() {
if (super.raffleActivity.getCount() > 0) {
// 设置发奖完成状态
super.raffleActivity.setState(super.raffleActivity.dispenseOutState);
super.raffleActivity.dispense();
}else {
System.out.println("奖品已经发放完毕");
}
}
}
/**
* 奖品发放完毕的状态类
*/
class DispenseOutState extends State{
public DispenseOutState(RaffleActivity raffleActivity) {
super(raffleActivity);
}
@Override
public void deductMoney() {
System.out.println("奖品已经发放完毕,无需扣除积分");
}
@Override
public boolean raffle() {
System.out.println("奖品已经发放完毕,不能抽奖");
return false;
}
@Override
public void dispensePrize() {
System.out.println("奖品已经发放完毕");
}
}
/**
* 不能抽奖的状态类
*/
class NoRaffleState extends State{
public NoRaffleState(RaffleActivity raffleActivity) {
super(raffleActivity);
}
@Override
public void deductMoney() {
System.out.println("扣除10个积分,可以抽奖了");
super.raffleActivity.setState(super.raffleActivity.canRaffleState);
}
@Override
public boolean raffle() {
System.out.println("不能抽奖了,无需抽奖");
return false;
}
@Override
public void dispensePrize() {
System.out.println("不能抽奖了,不能派发礼品");
}
}
class RaffleActivity{
// 礼物数量
private int count;
private State state;
public final State canRaffleState = new CanRaffleState(this);
public final State dispenseOutState = new DispenseOutState(this);
public final State dispenseState = new DispenseState(this);
public final State noRaffleState = new NoRaffleState(this);
public RaffleActivity(int count){
this.setCount(count);
setState(noRaffleState);
}
// 抽奖
public boolean raffle(){
// 开始抽奖
if(state.raffle()){
// 如果中奖了,则调用状态对应的发放礼品方法
state.dispensePrize();
return true;
}
return false;
}
// 扣除积分
public void deductMoney(){
this.state.deductMoney();
}
// 发放礼品
public void dispense(){
// 礼品减1
this.count = this.count - 1;
this.state.dispensePrize();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
public class Test {
public static void main(String[] args) {
RaffleActivity raffleActivity = new RaffleActivity(1);
for (int i = 0; i < 10; i++){
System.out.println("开始第【" + i + "】次抽奖");
// 扣除积分
raffleActivity.deductMoney();
// 开始抽奖,如果中奖就结束
if (raffleActivity.raffle()) {return;}
}
}
}
// 运行结果
开始第【0】次抽奖
扣除10个积分,可以抽奖了
正在抽奖
很遗憾没有中奖
......
很遗憾没有中奖
开始第【7】次抽奖
扣除10个积分,可以抽奖了
正在抽奖
恭喜您中奖了
奖品已经发放完毕
代码很多,我来精简下
package org.state.version1;
import java.util.Random;
abstract class State{
public RaffleActivity raffleActivity;
public State(RaffleActivity raffleActivity){
this.raffleActivity = raffleActivity;
}
// 扣除积分
public abstract void deductMoney();
// 抽奖动作
public abstract boolean raffle();
// 发放奖品
public abstract void dispensePrize();
}
/**
* 可以抽奖的状态类
*/
class CanRaffleState extends State{
public CanRaffleState(RaffleActivity raffleActivity) {
super(raffleActivity);
}
@Override
public boolean raffle() {
System.out.println("正在抽奖");
Random random = new Random();
if (random.nextInt(10) == 1){
System.out.println("恭喜您中奖了");
// 修改活动状态为发放奖品状态
super.raffleActivity.setState(super.raffleActivity.dispenseState);
return true;
}else {
System.out.println("很遗憾没有中奖");
super.raffleActivity.setState(super.raffleActivity.noRaffleState);
return false;
}
}
}
/**
* 奖品发放的状态类
*/
class DispenseState extends State{
@Override
public void dispensePrize() {
if (super.raffleActivity.getCount() > 0) {
// 设置发奖完成状态
super.raffleActivity.setState(super.raffleActivity.dispenseOutState);
super.raffleActivity.dispense();
}else {
System.out.println("奖品已经发放完毕");
}
}
}
class RaffleActivity{
// 礼物数量
private int count;
private State state;
public final State canRaffleState = new CanRaffleState(this);
public final State dispenseOutState = new DispenseOutState(this);
public final State dispenseState = new DispenseState(this);
public final State noRaffleState = new NoRaffleState(this);
public RaffleActivity(int count){
this.setCount(count);
setState(noRaffleState);
}
// 抽奖
public boolean raffle(){
// 开始抽奖
if(state.raffle()){
// 如果中奖了,则调用状态对应的发放礼品方法
state.dispensePrize();
return true;
}
return false;
}
}
// 另外两个装态没什么用,省略掉
public class Test {
public static void main(String[] args) {
RaffleActivity raffleActivity = new RaffleActivity(1);
for (int i = 0; i < 10; i++){
System.out.println("开始第【" + i + "】次抽奖");
// 扣除积分
raffleActivity.deductMoney();
// 开始抽奖,如果中奖就结束
if (raffleActivity.raffle()) {return;}
}
}
}
可以看到其实就是抽奖、发送奖品两个核心的点,生活中的抽奖无非也就是这样。比较核心的点就是抽奖的中奖逻辑以及中奖后的切换状态。每次抽奖都会调用raffle方法,当中奖后调用state.dispensePrize();此次的state已经被CanRaffleState类raffle方法变为super.raffleActivity.dispenseState(发放奖品状态)。状态的变化对于用户来说无感知,都是状态类内部动态修改。
使用状态模式的优点:
- **简化应用逻辑控制:**状态模式使用单独的类来封装一个状态的处理,可以把负责逻辑控制的代码分散到单独的状态类中去,这样就把着眼点从执行状态提高到整个对象的状态,使得代码结构化和意图更清晰,从而简化应用的逻辑控制。
- **更好地分离状态和行为:**状态模式通过设置所有状态类的公共接口,把状态和状态对应的行为分离开,把所有与一个特定的状态相关的行为都放入一个对象中,使得应用程序在控制的时候,只需要关心状态的切换,而不用关心这个状态对应的真正处理。
- **更好的扩展性:**引入了状态处理的公共接口后,使得扩展新的状态变得非常容易,只需要新增加一个实现状态处理的公共接口的实现类,然后在进行状态维护的地方,设置状态变化到这个新的状态即可。
- **显式化进行状态转换:**状态模式为不同的状态引入独立的对象,使得状态的转换变得更加明确。而且状态对象可以保证上下文不会发生内部状态不一致的情况,因为上下文中只有一个变量来记录状态对象,只要为这一个变量赋值就可以了。
使用状态模式的缺点:
- 一个状态对应一个状态处理类,会使得程序引入太多的状态类,这样程序变得杂乱。
下图是IDEA自动生成的UML图,可以和我们画的UML图对比下
三、JavaSE中状态模式的使用
暂时没发现,如果读者有发现,还请私信我添加,谢谢
四、Struts2状态模式的应用
暂时没发现,如果读者有发现,还请私信我添加,谢谢