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

我是荔园微风,作为一名在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,则账户的状态为正常状态(Normal State),此时用户既可以向该账户存款也可以从该账户取款; 如果账户中余额小于0,并且大于-10000,则账户的状态为透支状态(Overdraft State),此时用户既可以向该账户存款也可以从该账户取款,但需要按天计算利息;如果账户中余额等于-10000,那么账户的状态为受限状态(Restricted State),此时用户只能向该账户存款,不能再从中取款,同时也将按天计算利息。还有最后一种情况,如果取款导致金额小于-10000元了怎么办?我们这里要规定一下,如果小于这个数,马上宣布不正常状态不准用户操作。Check()用于在每一次执行存款和取款操作后根据余额来判断是否要进行状态转换并实现状态转换,相同的方法在不同的状态中可能会有不同的实现。为了实现不同状态下对象的各种行为以及对象状态之间的相互转化。

(1)Account:银行账户,环境类

package designpatterns.state;

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 + "开户,余额为" + this.balance);    
   }
    
   public double getBalance() {
      return this.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); //调用状态对象的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)AccountState:账户状态类,抽象状态类

package designpatterns.state;

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

(3)NormalState:正常状态类

package designpatterns.state;

public class NormalState extends AccountState {
   public NormalState(Account cp) {
        this.cp = cp;
    }
 
   public NormalState(AccountState 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 RestrictedState(this));
        }
        else if (cp.getBalance() < -10000) {
            System.out.println("不正常状态");
        }    
    }   
}  

(4)OverdraftState:透支状态类

package designpatterns.state;

public class OverdraftState extends AccountState {
   public OverdraftState(AccountState 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 NormalState(this));
        }
        else if (cp.getBalance() == -10000) {
            cp.setState(new RestrictedState(this));
        }
        else if (cp.getBalance() < -10000) {
            System.out.println("不正常状态");
        }    
    }   
}  

(5)RestrictedState:透支状态类

package designpatterns.state;

public class RestrictedState extends AccountState {
   public OverdraftState(AccountState 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 NormalState(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();
    }
}

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

状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/1241.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【完整代码】用HTML/CSS制作一个美观的个人简介网页

【完整代码】用HTML/CSS制作一个美观的个人简介网页整体结构完整代码用HTML/CSS制作一个美观的个人简介网页——学习周记1HELLO&#xff01;大家好&#xff0c;由于《用HTML/CSS制作一个美观的个人简介网页》这篇笔记有幸被很多伙伴关注&#xff0c;于是特意去找了之前写的完整…

【高阶数据结构】红黑树

文章目录1. 使用场景2. 性质3. 结点定义4. 结点旋转5. 结点插入1. 使用场景 Linux进程调度CFSNginx Timer事件管理Epoll事件块的管理 2. 性质 每一个节点是红色或者黑色根节点一定是黑色每个叶子节点是黑色如果一个节点是红色&#xff0c;那么它的两个儿子节点都是黑色从任意…

【基础算法】单链表的OJ练习(6) # 复制带随机指针的链表 #

文章目录&#x1f347;前言&#x1f34e;复制带随机指针的链表&#x1f351;写在最后&#x1f347;前言 本章的链表OJ练习&#xff0c;是最后的也是最难的。对于本题&#xff0c;我们不仅要学会解题的思路&#xff0c;还要能够通过这个思路正确的写出代码&#xff0c;也就是思路…

20230314整理

1.JVM内存区域 程序计数器&#xff1a;字节码解释器通过改变程序计数器来依次读取指令&#xff0c;在多线程的情况下&#xff0c;程序计数器用于记录当前线程执行的位置&#xff0c;从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。它的生命周期随着线程的创建而创…

基于Java+SpringBoot+vue的学生成绩管理系统设计和实现【源码+论文+演示视频+包运行成功】

博主介绍&#xff1a;专注于Java技术领域和毕业项目实战 &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例&#xff08;200套&#xff09; 目录 一、效果演示 二、…

扯什么 try-catch 性能问题?

“yes&#xff0c;你看着这鬼代码&#xff0c;竟然在 for 循环里面搞了个 try-catch&#xff0c;不知道try-catch有性能损耗吗&#xff1f;”老陈煞有其事地指着屏幕里的代码&#xff1a; for (int i 0; i < 5000; i) {try {dosth} catch (Exception e) {e.printStackTrace…

如何测试一个AI系统?

最近AI大火&#xff0c;chatgpt、GPT-4、文心一言不断的在轰炸着我们的生活、工作&#xff0c;很多时候我们都在感叹这智能化来的太快了。对于一个测试工程师&#xff0c;如何开始测试一个AI系统呢&#xff0c;今天我们就一起来聊聊相关的内容。 智能系统对测试工程师提出的新问…

2023年网络安全比赛--网络安全事件响应中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 1.找出黑客植入到系统中的二进制木马程序,并将木马程序的名称作为Flag值(若存在多个提交时使用英文逗号隔开,例如bin,sbin,…)提交; 2.找出被黑客修改的系统默认指令,并将被修改的指令里最后一个单词作为Flag值提交; 3.找出…

React 用一个简单案例体验一遍 React-dom React-router React-redux 全家桶

一、准备工作 本文略长&#xff0c;建议耐心读完&#xff0c;每一节的内容与上一节的内容存在关联&#xff0c;最好跟着案例过一遍&#xff0c;加深记忆。 1.1 创建项目 第一步&#xff0c;执行下面的命令来创建一个 React 项目。 npx create-react-app react-example cd rea…

Springboot集成Swagger

一、Swagger简介注意点&#xff01; 在正式发布的时候要关闭swagger&#xff08;出于安全考虑&#xff0c;而且节省内存空间&#xff09;之前开发的时候&#xff0c;前端只用管理静态页面&#xff0c; http请求到后端&#xff0c; 模板引擎JSP&#xff0c;故后端是主力如今是前…

【宝塔面板部署nodeJs项目】网易云nodeJs部署在云服务器上,保姆级教程,写网易云接口用自己的接口不受制于人

看了很多部署的&#xff0c;要么少步骤&#xff0c;要么就是写的太简洁&#xff0c;对新手不友好 文章目录前言一、下载网易云nodejs项目1. git clone下载&#xff0c;两种方式2. 运行项目二、使用步骤1. 先在本地运行2.测试接口三、部署服务器1. 在宝塔面板安装pm2管理器2. 压…

字符函数和字符串函数【上篇】

文章目录&#x1f396;️1.函数介绍&#x1f4ec;1.1. strlen&#x1f4ec;1.2. strcpy&#x1f4ec;1.3. strcat&#x1f4ec;1.4. strcmp&#x1f4ec;1.5. strncpy&#x1f4ec;1.6. strncat&#x1f4ec;1.7. strncmp&#x1f396;️1.函数介绍 &#x1f4ec;1.1. strlen …

入行 5年,跳槽 3次,我终于摸透了软件测试这行(来自过来人的忠告)

目录 前言 第一年 第二年 第三年 第四年 作为过来人的一些忠告 前言 最近几年行业在如火如荼的发展壮大&#xff0c;以及其他传统公司都需要大批量的软件测试人员&#xff0c;但是20年的疫情导致大规模裁员&#xff0c;让人觉得行业寒冬已来&#xff0c;软件测试人员的职…

【YOLOv8/YOLOv7/YOLOv5/YOLOv4/Faster-rcnn系列算法改进NO.60】损失函数改进为wiou

前言作为当前先进的深度学习目标检测算法YOLOv8&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系列文章&#xff0c;将重点对YOLOv8的如何改进进行详细的介绍&…

css属性学习

css属性 就是我们选择器里面 { } 中的内容 字体样式 font-size 控制字体大小&#xff1a;单位px&#xff08;像素&#xff09; 默认字体占16个像素 <p style"font-size:30px;">font-size&#xff1a;字体大小&#xff0c;单位的话可以用px表示占的像素个数&…

Mini-Xml 经典实例Demo

欢迎小伙伴的点评✨✨&#xff0c;相互学习、博主将自己研发xml微型服务器的经验与之分享&#x1f30f;&#x1f30f;&#x1f642; 文章目录前言一、使用mxml库编写Xml文件1.1 经典实例Demo_11.2、经典实例Demo_21.3、经典实例Demo_3二、总结前言 本章将会给大家带来mini-xml…

在我的MacBook上捣鼓ESP8266

周三是我们的篮球日&#xff0c;打篮球后总是会有些兴奋&#xff0c;然后就容易睡不着&#xff0c;额&#xff0c;睡不着就拿我的ESP8266开发板出来捣鼓一下。先下载编译工具链https://github.com/espressif/ESP8266_RTOS_SDK下载sdkgit clone https://github.com/espressif/ES…

C++程序在内存中的模型

进程&#xff08;Process&#xff09;是计算机中的程序&#xff0c;数据集合在其上运行的一次活动&#xff0c;是系统进行资源分配的基本单位。每个进程都有自己独立的虚拟内存地址空间&#xff0c;这个虚拟的内存地址空间一般是线性连续的&#xff0c;这个内存地址空间是虚拟的…

面试官想看我写一篇关于“原型链”和“构造函数”深入理解的文章

前言&#xff1a; 在参加工作的面试过程中&#xff0c;我搬出了我的个人掘金账号写在了简历里&#xff0c;面试官很感兴趣&#xff0c;他不仅关注了我的账号&#xff0c;还想让我写一篇《原型链》的见解&#xff0c;由于老早就想总结一篇关于原型的文章&#xff0c;奈何自己刚开…

07平衡负载:gRPC是如何进行负载均衡的?

负载均衡(Load Balance),其含义就是指将请求负载进行平衡、分摊到多个负载单元上进行运行,从而协同完成工作任务。 负载均衡的主要作用: 提升并发性能:负载均衡通过算法尽可能均匀的分配集群中各节点的工作量,以此提高集群的整体的吞吐量。 提供可伸缩性:可添加或减少服…
最新文章