行为型模式---状态模式
1. 状态模式的概述
状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题,当系统中某个对象存在多个状态,这些状态之间可进行转换,而且对象在不同状态下的行为不相同时可以使用状态模式;状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象的状态可以灵活变化,客户端无须关心对象状态的转换以及对象所处的当前状态;
2. 状态模式中的角色:
1) Context(环境类): 它是拥有多种状态的对象,由于环境类的状态存在多样性且不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类,在环境类中维护一个抽象状态类State的实例,定义当前的状态;
2) State(抽象状态类): 用于定义一个接口封装与环境类一个特定状态相关的行为,在抽象类中声明各种不同状态对应的方法,而在其子类中不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中;
3) ConcreteState(具体状态类): 每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同;
3. 核心代码
//抽象状态类
abstract class State {
//抽象业务方法
public abstract void handle();
}
//具体状态类
class ConcreteState extends State {
public void handle() {...}
}
//环境类维持一个对抽象状态类的引用,通过setState()方法可以向环境类注入不同的状态对象,再在环境类的业务方法中调用状态对象的方法
class Context {
private State state; //维持一个对抽象状态的对象的引用
private int value; //其他属相值,该属性值的变化可能会导致对象状态发生变化
public void setState(State state) {
this.state = state;
}
public void request(){
state.handle();
}
}
在实际使用中,State与Context之间可能存在依赖或者关联关系.一个对象的状态之间还可以进行相互转换,转换方式有:
- 统一由环境类来负责状态之间的转换,在环境类的业务方法中通过对某些属性值判断实现状态转换;
public void changeState() { //根据属性值进行状态转换 if(value == 0) { this.setState(new ConcreStateA()); }else if (value == 1) { this.setState(new ConcreteStateB()); } } |
4. 案例
1) 解决方案
2) 代码实现
package com.zach.pattern.state;
/**
* 账户类: 充当环境角色
* @author Zach
*
*/
public class Account {
private AccountState state; //维持一个对抽象状态类的引用
private String owner; //开户名
private double balance = 0; //账户余额
public Account(String owner,double init){
this.owner=owner;
this.balance = init;
this.state = new NormalState(this); //设置初始状态
System.out.println(this.owner +"开户,初始金额为"+init);
System.out.println("=============================");
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void setState(AccountState state) {
this.state = state;
}
public void deposit(double amount) {
System.out.println(this.owner+"存款"+amount);
state.deposit(amount);
System.out.println("现在余额为"+this.balance);
System.out.println("现在用户状态为: "+this.state.getClass().getName());
System.out.println("==========================");
}
public void withdraw(double amount) {
System.out.println(this.owner+"取款"+amount);
state.withdraw(amount); //调用状态对象的withdraw
System.out.println("现在的余额为"+this.balance);
System.out.println("现在用户状态为: "+this.state.getClass().getName());
System.out.println("==========================");
}
public void computeInterest() {
state.computeInterest();
}
}
package com.zach.pattern.state;
/**
* 抽象状态类
* @author Zach
*
*/
public abstract class AccountState {
protected Account acc;
public abstract void deposit(double amount);
public abstract void withdraw(double amount);
public abstract void computeInterest();
public abstract void stateCheck();
}
package com.zach.pattern.state;
/**
* 正常状态类: 具体装态类
* @author Zach
*
*/
public class NormalState extends AccountState{
public NormalState(Account acc){
this.acc = acc;
}
public NormalState(AccountState state) {
this.acc = state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
acc.setBalance(acc.getBalance()-amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("正常状态,无须支付利息!");
}
/**
* 状态转换
*/
@Override
public void stateCheck() {
if(acc.getBalance()>-2000 && acc.getBalance()<=0)
acc.setState(new OverdraftState(this));
else if (acc.getBalance()==-2000){
acc.setState(new RestrictedState(this));
}else if (acc.getBalance()<-2000)
System.out.println("操作受限!");
}
}
package com.zach.pattern.state;
public class OverdraftState extends AccountState {
public OverdraftState(AccountState state) {
this.acc = state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
acc.setBalance(acc.getBalance()-amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("计算利息!");
}
@Override
public void stateCheck() {
if(acc.getBalance()>0){
acc.setState(new NormalState(this));
}else if (acc.getBalance()==-2000){
acc.setState(new RestrictedState(this));
}else if (acc.getBalance()<-2000){
System.out.println("操作受限!");
}
}
}
package com.zach.pattern.state;
/**
* @Author:Zach
* @Description: 受限状态,具体状态类
* @Date:Created in 14:24 2018/8/6
* @Modified By:
*/
public class RestrictedState extends AccountState{
public RestrictedState(AccountState state) {
this.acc = state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
System.out.println("账号受限,取款失败");
}
@Override
public void computeInterest() {
System.out.println("计算利息!");
}
@Override
public void stateCheck() {
if (acc.getBalance()>0) {
acc.setState(new NormalState(this));
}
else if (acc.getBalance()>-2000) {
acc.setState(new OverdraftState(this));
}
}
}
package com.zach.pattern.state;
/**
* @Author:Zach
* @Description: 客户端类
* @Date:Created in 14:50 2018/8/6
* @Modified By:
*/
public class Client {
public static void main(String[] args){
Account account = new Account("Zach",0.0);
account.deposit(1000);
account.withdraw(2000);
account.deposit(3000);
account.withdraw(4000);
account.withdraw(1000);
account.computeInterest();
}
}
运行结果
Zach开户,初始金额为0.0
=============================
Zach存款1000.0
现在余额为1000.0
现在用户状态为: com.zach.pattern.state.NormalState
==========================
Zach取款2000.0
现在的余额为-1000.0
现在用户状态为: com.zach.pattern.state.OverdraftState
==========================
Zach存款3000.0
现在余额为2000.0
现在用户状态为: com.zach.pattern.state.NormalState
==========================
Zach取款4000.0
现在的余额为-2000.0
现在用户状态为: com.zach.pattern.state.RestrictedState
==========================
Zach取款1000.0
账号受限,取款失败
现在的余额为-2000.0
现在用户状态为: com.zach.pattern.state.RestrictedState
==========================
计算利息!
5. 共享状态
在某些情况下,多个环境对象可能需要共享同一个状态,此时需要将这些状态对象定义为环境类的静态成员对象
1) 案例
如果某系统要求两个开关对象要么都处于开的状态,要么都处于关的状态,在使用时它们的状态保持一致,开关可以由开转换到关,也可以由关转换到开
2) 代码实现
package com.zach.pattern.state.share;
/**
* @Author:Zach
* @Description:
* @Date:Created in 15:39 2018/8/6
* @Modified By:
*/
public class Switch {
private static State state,onState,offState;
private String name;
public Switch(String name) {
this.name = name;
onState = new OnState();
offState = new OffState();
this.state = onState;
}
public void setState(State state) {
this.state = state;
}
public static State getState(String type) {
if("on".equals(type)){
return onState;
}else {
return offState;
}
}
//打开开关
public void on(){
System.out.println(name);
state.on(this);
}
//关闭开关
public void off() {
System.out.println(name);
state.off(this);
}
}
package com.zach.pattern.state.share;
/**
* @Author:Zach
* @Description:
* @Date:Created in 15:40 2018/8/6
* @Modified By:
*/
public abstract class State {
public abstract void on(Switch s);
public abstract void off(Switch s);
}
package com.zach.pattern.state.share;
/**
* @Author:Zach
* @Description:
* @Date:Created in 15:43 2018/8/6
* @Modified By:
*/
public class OnState extends State {
@Override
public void on(Switch s) {
System.out.println("已经打开!");
}
@Override
public void off(Switch s) {
System.out.println("关闭");
s.setState(Switch.getState("off"));
}
}
package com.zach.pattern.state.share;
/**
* @Author:Zach
* @Description:
* @Date:Created in 15:51 2018/8/6
* @Modified By:
*/
public class OffState extends State {
@Override
public void on(Switch s) {
System.out.println("打开");
s.setState(Switch.getState("on"));
}
@Override
public void off(Switch s) {
System.out.println("已经关闭!");
}
}
package com.zach.pattern.state.share;
/**
* @Author:Zach
* @Description:
* @Date:Created in 16:05 2018/8/6
* @Modified By:
*/
public class Client {
public static void main(String[] args){
Switch s1 = new Switch("开关1");
Switch s2 = new Switch("开关2");
s1.on();
s2.on();
s1.off();
s2.off();
s2.on();
s1.on();
}
}
运行结果
开关1
已经打开!
开关2
已经打开!
开关1
关闭
开关2
已经关闭!
开关2
打开
开关1
已经打开!
6. 使用环境类实现状态转换
在状态模式中实现状态的转换时,具体状态类可通过调用环境类Context的setState()进行状态转换,也可以由环境类统一实现状态转换,此时增加新的具体状态类可能需要修改其他具体状态类或者环境类的源代码,这两种方式对客户端都是透明的,
1) 案例
2) 实现代码
package com.zach.pattern.state.context;
/**
* @Author:Zach
* @Description: 屏幕类
* @Date:Created in 16:43 2018/8/6
* @Modified By:
*/
public class Screen {
private State currentState,normalState,largerState,largestState;
public Screen(){
this.normalState = new NormalState();
this.largerState = new LargerState();
this.largestState = new LargestState();
this.currentState = normalState;
this.currentState.display();
}
public void setState(State state) {
this.currentState = state;
}
//单击事件处理方法,封装了对状态类中业务方法的调用和状态的转换
public void onClick(){
if(this.currentState == normalState) {
this.setState(largerState);
this.currentState.display();
}
else if(this.currentState == largerState){
this.setState(largestState);
this.currentState.display();
}
else if (this.currentState == largestState) {
this.setState(normalState);
this.currentState.display();
}
}
}
package com.zach.pattern.state.context;
/**
* @Author:Zach
* @Description: 抽象状态类
* @Date:Created in 16:34 2018/8/6
* @Modified By:
*/
public abstract class State {
public abstract void display();
}
package com.zach.pattern.state.context;
/**
* @Author:Zach
* @Description: 正常状态类
* @Date:Created in 16:35 2018/8/6
* @Modified By:
*/
public class NormalState extends State {
@Override
public void display() {
System.out.println("正常大小!");
}
}
package com.zach.pattern.state.context;
/**
* @Author:Zach
* @Description: 4倍状态类
* @Date:Created in 16:35 2018/8/6
* @Modified By:
*/
public class LargestState extends State {
@Override
public void display() {
System.out.println("4倍大小!");
}
}
package com.zach.pattern.state.context;
/**
* @Author:Zach
* @Description: 2倍状态类
* @Date:Created in 16:35 2018/8/6
* @Modified By:
*/
public class LargerState extends State {
@Override
public void display() {
System.out.println("2倍大小!");
}
}
package com.zach.pattern.state.context;
/**
* @Author:Zach
* @Description:
* @Date:Created in 16:56 2018/8/6
* @Modified By:
*/
public class Client {
public static void main(String[] args){
Screen screen = new Screen();
screen.onClick();
screen.onClick();
screen.onClick();
}
}
7. 状态模式总结
1. 主要优点
1) 封装状态的转换规则,对状态转换代码进行集中管理,而不是分散到业务方法中;
2) 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可是环境对象拥有不同的行为;
3) 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块
4) 可以让多个环境对象共享一个状态对象;
2. 主要缺点
1) 增加系统设计的难度
2) 对开闭原则的支持不好
3. 适用场景
1) 对象的行为依赖它的状态,状态的改变将导致行为的变化
2) 在代码中包含大量与对象状态有关的条件语句
上一篇: 微信小程序练习demo