Windows环境下实现设计模式——状态模式(JAVA版)

本文介绍了如何在Windows操作系统上使用JAVA编程实现状态模式,这是一种行为型设计模式,允许对象在其内部状态改变时改变行为。文章通过生活中的情绪变化例子解释了状态模式的概念,并详细展示了如何通过创建环境类、抽象状态类和具体状态类来封装和转换状态。此外,还提供了金融理财APP账户状态管理的示例,解释了如何在不同状态间进行转换,并对比了状态模式与策略模式的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现状态模式(设计模式)。

不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。

我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个状态模式,真实的实现一个,你看懂代码后,自然就明白了。

状态模式State Pattern(行为型设计模式)

定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。

在日常生活中,人们总是有些各种喜怒哀乐的表情,如上图,而且这四种状态还会互相转化。有的时候人们会转怒为喜,有的时候会得意忘形,有的时候会遇到惊喜突然间高兴起来。各种情绪会互相转化。包括人在内,很多事物都具有多种状态,而且在不同状态下会具有不同的行为,这些状态在特定条件下还将发生相互转换。而状态模式就是要模拟这种同一事物存在多种状态但又会变化转化的情况。

状态模式就是用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中的某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。

在软件系统中,有些对象具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。通常可以使用复杂的条件判断语句if else之类来进行状态判断和转换操作,但这样会导致代码的可维护性和灵活性下降。

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。

在状态模式结构图中包含如下三个角色:Context(环境类)、State(抽象状态类)、ConcreteState(具体状态类)。环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。抽象状态类用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。具体状态类是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

JAVA代码

public abstract class State {
    //声明抽象业务方法,不同的具体状态类可以不同的实现
    public abstract void handle();
}

public class ConcreteState extends State {
    public void handle() {
        //方法具体实现代码
    }
}

public class Context {
    private State state; //维持一个对抽象状态对象的引用
    private int value; //其他属性值,该属性值的变化可能会导致对象状态发生变化
 
    //设置状态对象
    public void setState(State state) {
        this.state = state;
    }
 
    public void request() {
        //其他代码
        state.handle(); //调用状态对象的业务方法
        //其他代码
    }
}

Context(环境类)实际上是真正拥有状态的对象,这个模式的主要精神就是将Context(环境类)中与状态有关的代码提取出来封装到专门的状态类中。在状态模式结构图中,在Context中定义了一个State对象。Context(环境类)与抽象状态类State之间存在单向关联关系,也可能存在依赖或者其他关联关系。

好,下面是关键的内容了,这部分是这个模式和其他设计模式一个很大的不同点,也是此模式复杂的地方,也就是在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换,通常有两种实现状态转换的方式:

(1) 统一由环境类来负责状态之间的转换,此时,环境类还充当了状态管理器(State Manager)角色,在环境类的业务方法中通过对某些属性值的判断if else之类实现状态转换,还可以提供一个专门的方法用于实现属性判断和状态转换,如下所示:

public void changeState() {
     //判断属性值,根据属性值进行状态转换
     if (value == 0) {
          this.setState(new ConcreteStateA());
      }
      else if (value == 1) {
          this.setState(new ConcreteStateB());
      }
        ......
}

(2) 由具体状态类来负责状态之间的转换,可以在具体状态类的业务方法中判断环境类的某些属性值再根据情况为环境类设置新的状态对象,实现状态转换,同样,也可以提供一个专门的方法来负责属性值的判断和状态转换。此时,状态类与环境类之间就将存在依赖或关联关系,因为状态类需要访问环境类中的属性值,如下所示:

public void changeState(Context su) {
      //根据环境对象中的属性值进行状态转换
      if (su.getValue() == 1) {
          su.setState(new ConcreteStateB());
      }
      else if (su.getValue() == 2) {
          su.setState(new ConcreteStateC());
      }
        ......
}

应用实例

某金融理财手机APP具有理财功能,每位用户在其中会开通个人账户(Account),APP用户账户存在四种状态,如果账户中余额大于等于0,则账户的状态为普通情况状态(Regular State),此时用户既可以向该账户存款也可以从该账户取款; 如果账户中余额小于0,并且大于-10000,则账户的状态为透支状态(Overdraft State),此时用户既可以向该账户存款也可以从该账户取款,但需要按天计算利息;如果账户中余额等于-10000,那么账户的状态为冻结状态(Freeze State),此时用户只能向该账户存款,不能再从中取款。还有最后一种情况,如果取款导致金额小于-10000元了怎么办?我们这里要规定一下,如果小于这个数,马上宣布不正常状态不准用户操作。Check()用于在每一次执行存款和取款操作后根据余额来判断是否要进行状态转换并实现状态转换,相同的方法在不同的状态中可能会有不同的实现。为了实现不同状态下对象的各种行为以及对象状态之间的相互转化。

当看到这个应用开发任务后,我们一般会想到用分支逻辑法去做,通常我们在实现这类系统会使用到很多switch/case语句,case某种状态,发生什么动作,case另一种状态,则发生另外一种状态,但是这种方式会有问题,当状态数据不是很多的时候,switch/case可能可以搞定。但是当状态数目很多的时候(实际系统重也正是如此),维护一个大组switch/case语句将是一件异常困难并且容易出错的事情。状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。

(1)Account:金融理财手机APP账户,环境类

package designpatterns.state;

public class Account {
   private CashState state;   //维持一个对抽象状态对象的引用
   private String owner;   //开户名
   private double balance = 0;   //账户余额
    
   public Account(String owner,double init) {
      this.owner = owner;
      this.balance = init;
      this.state = new RegularState(this); //设置初始状态
      System.out.println(this.owner + "开户,余额为" + this.balance);    
   }
    
   public double getBalance() {
      return this.balance;
   }
    
   public void setBalance(double balance) {
       this.balance = balance;
   }
    
   public void setState(CashState state) {
        this.state = state;
    }
    
   public void deposit(double amount) {
        System.out.println(this.owner + "存款" + amount);
        state.deposit(amount); //调用状态对象的deposit()方法
        System.out.println("余额为"+ this.balance);
        System.out.println("帐户状态为"+ this.state.getClass().getName());
    }
    
    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());        
    }
    
    public void Interest()
    {
        state.Interest(); //调用状态对象的Interest()方法
    }
}

(2)CashState:账户状态类,抽象状态类

package designpatterns.state;

public abstract class CashState {
    protected Account cp;
    public abstract void deposit(double amount);
    public abstract void withdraw(double amount);
    public abstract void Interest();
    public abstract void Check();
}

(3)RegularState:普通情况状态类

package designpatterns.state;

public class RegularState extends CashState {
   public RegularState(Account cp) {
        this.cp = cp;
    }
 
   public RegularState(CashState state) {
        this.cp = state.cp;
    }
        
   public void deposit(double amount) {
        cp.setBalance(cp.getBalance() + amount);
        Check();
    }
    
   public void withdraw(double amount) {
        cp.setBalance(cp.getBalance() - amount);
        Check();
    }
    
    public void Interest()
    {
        System.out.println("正常状态");
    }
    
    //状态转换
    public void Check() {
        if (cp.getBalance() > -10000 && cp.getBalance() <= 0) {
            cp.setState(new OverdraftState(this));
        }
        else if (cp.getBalance() == -10000) {
            cp.setState(new FreezeState(this));
        }
        else if (cp.getBalance() < -10000) {
            System.out.println("不正常状态");
        }    
    }   
}  

(4)OverdraftState:透支状态类

package designpatterns.state;

public class OverdraftState extends CashState {
   public OverdraftState(CashState state) {
        this.cp = state.cp;
    }
        
   public void deposit(double amount) {
        cp.setBalance(cp.getBalance() + amount);
        Check();
    }
    
   public void withdraw(double amount) {
        cp.setBalance(cp.getBalance() - amount);
        Check();
    }
    
    public void Interest()
    {
        System.out.println("利息数额");
    }
    
    //状态转换
    public void Check() {
        if (cp.getBalance() > 0) {
            cp.setState(new RegularState(this));
        }
        else if (cp.getBalance() == -10000) {
            cp.setState(new FreezeState(this));
        }
        else if (cp.getBalance() < -10000) {
            System.out.println("不正常状态");
        }    
    }   
}  

(5)FreezeState:冻结状态类

package designpatterns.state;

public class FreezeState extends CashState {
   public FreezeState(CashState state) {
        this.cp = state.cp;
    }
        
   public void deposit(double amount) {
        cp.setBalance(cp.getBalance() + amount);
        Check();
    }
    
   public void withdraw(double amount) {
        System.out.println("不能");
    }
    
    public void Interest()
    {
        System.out.println("利息数额");
    }
    
    //状态转换
    public void Check() {
        if (cp.getBalance() > 0) {
            cp.setState(new RegularState(this));
        }
        else if (cp.getBalance() > -10000) {
            cp.setState(new OverdraftState(this));
        }
    }   
}  

(6)Client:客户端测试类

package designpatterns.state;

public class Client {
    public static void main(String args[]) {
        Account cp = new Account("小牛",0.0);
        cp.deposit(5000);
        cp.withdraw(10000);
        cp.deposit(6000);
        cp.withdraw(4000);
        cp.Interest();
    }
}

这篇设计模式文章是我写的最累的一篇了,写到这里已经很晚了,实在写不动了。输出结果大家自行推理就是了。无非就是随着用户又取钱又存钱的导致银行帐户出现各类状态转换问题。

状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是不清楚的,这样使得客户端和前面的环境类等都是松耦合,方便客户端的使用。在实际开发中,状态模式在很多软件开发工作中使用,在政府公文工作流和商业游戏开发中都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。

有的初学者会认为这个状态模式和策略模式好像啊,其实他们还是有区别的,不是一回事。首先,策略模式封装了一组行为或者算法,它允许Client在运行时动态的切换,是一种操作外部的行为。而状态模式是帮助一个类在不同的状态下显示不同的行为,依赖于内部的状态。策略模式不持有Context的引用,而是被Context所使用;状态模式的每个状态都持有Context的引用,从而在Context中实现状态的转移。从理论上说,策略模式定义对象应该“怎么做”;状态模式定义了对象“是什么”,“什么时候做”。如果要用一句说来说就是下面这句:

状态模式中的行为是平行的,不可替换的,策略模式中的行为是彼此独立的,可以相互替换的。

状态模式将对象的行为封装在不同的状态对象中,将对象的状态从对象中分离出来,客户端无需关心对象的当前状态和状态的转换。状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。具体可以看我另外一篇文章,如下:

Windows环境下实现设计模式——策略模式(JAVA版)

各位小伙伴,这次我们就说到这里,下次我们再深入研究windows环境下的各类设计模式实现。

作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值