java 状态模式 同步_JAVA设计模式之状态模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述状态(State)模式的:

状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式。

状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。

状态模式的结构

用一句话来表述,状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式的示意性类图如下所示:

cYqyRcVcaC4AAAAASUVORK5CYII=

状态模式所涉及到的角色有:

●  环境(Context)角色,也成上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。

●  抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。

●  具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。

源代码

环境角色类

48304ba5e6f9fe08f3fa1abda7d326ab.png

public classContext {//持有一个State类型的对象实例

privateState state;public voidsetState(State state) {this.state =state;

}/*** 用户感兴趣的接口方法*/

public voidrequest(String sampleParameter) {//转调state来处理

state.handle(sampleParameter);

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

抽象状态类

48304ba5e6f9fe08f3fa1abda7d326ab.png

public interfaceState {/*** 状态对应的处理*/

public voidhandle(String sampleParameter);

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

具体状态类

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class ConcreteStateA implementsState {

@Overridepublic voidhandle(String sampleParameter) {

System.out.println("ConcreteStateA handle :" +sampleParameter);

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class ConcreteStateB implementsState {

@Overridepublic voidhandle(String sampleParameter) {

System.out.println("ConcreteStateB handle :" +sampleParameter);

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

客户端类

48304ba5e6f9fe08f3fa1abda7d326ab.png

public classClient {public static voidmain(String[] args){//创建状态

State state = newConcreteStateB();//创建环境

Context context = newContext();//将状态设置到环境中

context.setState(state);//请求

context.request("test");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

从上面可以看出,环境类Context的行为request()是委派给某一个具体状态类的。通过使用多态性原则,可以动态改变环境类Context的属性State的内容,使其从指向一个具体状态类变换到指向另一个具体状态类,从而使环境类的行为request()由不同的具体状态类来执行。

使用场景

考虑一个在线投票系统的应用,要实现控制同一个用户只能投一票,如果一个用户反复投票,而且投票次数超过5次,则判定为恶意刷票,要取消该用户投票的资格,当然同时也要取消他所投的票;如果一个用户的投票次数超过8次,将进入黑名单,禁止再登录和使用系统。

要使用状态模式实现,首先需要把投票过程的各种状态定义出来,根据以上描述大致分为四种状态:正常投票、反复投票、恶意刷票、进入黑名单。然后创建一个投票管理对象(相当于Context)。

系统的结构图如下所示:

B3EKJ9Ez6uZwAAAAAElFTkSuQmCC

源代码

抽象状态类

48304ba5e6f9fe08f3fa1abda7d326ab.png

public interfaceVoteState {/*** 处理状态对应的行为

*@paramuser 投票人

*@paramvoteItem 投票项

*@paramvoteManager 投票上下文,用来在实现状态对应的功能处理的时候,

* 可以回调上下文的数据*/

public voidvote(String user,String voteItem,VoteManager voteManager);

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

具体状态类——正常投票

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class NormalVoteState implementsVoteState {

@Overridepublic voidvote(String user, String voteItem, VoteManager voteManager) {//正常投票,记录到投票记录中

voteManager.getMapVote().put(user, voteItem);

System.out.println("恭喜投票成功");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

具体状态类——重复投票

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class RepeatVoteState implementsVoteState {

@Overridepublic voidvote(String user, String voteItem, VoteManager voteManager) {//重复投票,暂时不做处理

System.out.println("请不要重复投票");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

具体状态类——恶意刷票

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class SpiteVoteState implementsVoteState {

@Overridepublic voidvote(String user, String voteItem, VoteManager voteManager) {//恶意投票,取消用户的投票资格,并取消投票记录

String str =voteManager.getMapVote().get(user);if(str != null){

voteManager.getMapVote().remove(user);

}

System.out.println("你有恶意刷屏行为,取消投票资格");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

具体状态类——黑名单

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class BlackVoteState implementsVoteState {

@Overridepublic voidvote(String user, String voteItem, VoteManager voteManager) {//记录黑名单中,禁止登录系统

System.out.println("进入黑名单,将禁止登录和使用本系统");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

环境类

48304ba5e6f9fe08f3fa1abda7d326ab.png

public classVoteManager {//持有状体处理对象

private VoteState state = null;//记录用户投票的结果,Map对应Map

private Map mapVote = new HashMap();//记录用户投票次数,Map对应Map

private Map mapVoteCount = new HashMap();/*** 获取用户投票结果的Map*/

public MapgetMapVote() {returnmapVote;

}/*** 投票

*@paramuser 投票人

*@paramvoteItem 投票的选项*/

public voidvote(String user,String voteItem){//1.为该用户增加投票次数//从记录中取出该用户已有的投票次数

Integer oldVoteCount =mapVoteCount.get(user);if(oldVoteCount == null){

oldVoteCount= 0;

}

oldVoteCount+= 1;

mapVoteCount.put(user, oldVoteCount);//2.判断该用户的投票类型,就相当于判断对应的状态//到底是正常投票、重复投票、恶意投票还是上黑名单的状态

if(oldVoteCount == 1){

state= newNormalVoteState();

}else if(oldVoteCount > 1 && oldVoteCount < 5){

state= newRepeatVoteState();

}else if(oldVoteCount >= 5 && oldVoteCount <8){

state= newSpiteVoteState();

}else if(oldVoteCount > 8){

state= newBlackVoteState();

}//然后转调状态对象来进行相应的操作

state.vote(user, voteItem, this);

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

客户端类

48304ba5e6f9fe08f3fa1abda7d326ab.png

public classClient {public static voidmain(String[] args) {

VoteManager vm= newVoteManager();for(int i=0;i<9;i++){

vm.vote("u1","A");

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行结果如下:

4NZPzh46kzkAAAAASUVORK5CYII=

从上面的示例可以看出,状态的转换基本上都是内部行为,主要在状态模式内部来维护。比如对于投票的人员,任何时候他的操作都是投票,但是投票管理对象的处理却不一定一样,会根据投票的次数来判断状态,然后根据状态去选择不同的处理。

认识状态模式

●  状态和行为

所谓对象的状态,通常指的就是对象实例的属性的值;而行为指的就是对象的功能,再具体点说,行为大多可以对应到方法上。

状态模式的功能就是分离状态的行为,通过维护状态的变化,来调用不同状态对应的不同功能。也就是说,状态和行为是相关联的,它们的关系可以描述为:状态决定行为。

由于状态是在运行期被改变的,因此行为也会在运行期根据状态的改变而改变。

●  行为的平行性

注意平行线而不是平等性。所谓平行性指的是各个状态的行为所处的层次是一样的,相互独立的、没有关联的,是根据不同的状态来决定到底走平行线的哪一条。行为是不同的,当然对应的实现也是不同的,相互之间是不可替换的。

wMDA2yr1ZJSYQAAAABJRU5ErkJggg==

而平等性强调的是可替换性,大家是同一行为的不同描述或实现,因此在同一个行为发生的时候,可以根据条件挑选任意一个实现来进行相应的处理。

Q46dQ6AAAAAElFTkSuQmCC

大家可能会发现状态模式的结构和策略模式的结构完全一样,但是,它们的目的、实现、本质却是完全不一样的。还有行为之间的特性也是状态模式和策略模式一个很重要的区别,状态模式的行为是平行性的,不可相互替换的;而策略模式的行为是平等性的,是可以相互替换的。

●   环境和状态处理对象

在状态模式中,环境(Context)是持有状态的对象,但是环境(Context)自身并不处理跟状态相关的行为,而是把处理状态的功能委托给了状态对应的状态处理类来处理。

在具体的状态处理类中经常需要获取环境(Context)自身的数据,甚至在必要的时候会回调环境(Context)的方法,因此,通常将环境(Context)自身当作一个参数传递给具体的状态处理类。

客户端一般只和环境(Context)交互。客户端可以用状态对象来配置一个环境(Context),一旦配置完毕,就不再需要和状态对象打交道了。客户端通常不负责运行期间状态的维护,也不负责决定后续到底使用哪一个具体的状态处理对象。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python列表修改_python修改列表

广告关闭 腾讯云11.11云上盛惠 &#xff0c;精选热门产品助力上云&#xff0c;云服务器首年88元起&#xff0c;买的越多返的越多&#xff0c;最高返5000元&#xff01; 由于惯性思维&#xff0c;导致使用for循环修改列表中的值出现问题首次尝试&#xff1a;def make_great(orig…

QPW 企业员工表(tf_company_employee)

文章目录员工表字段说明员工表 CREATE TABLE tf_company_employee (employee_id bigint(20) NOT NULL COMMENT 职员Id,employee_no varchar(15) NOT NULL COMMENT 职员编号, # &#xff08;补&#xff09;管理员自定义&#xff0c;可以为空&#xff0c;但是非空时不许重复&…

python装饰器作用和功能_Python装饰器原理与用法分析

这篇文章主要介绍了Python装饰器原理与用法,结合实例形式分析了Python装饰器的概念、原理、使用方法及相关操作注意事项,需要的朋友可以参考下 本文实例讲述了Python装饰器原理与用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a; 1、装饰器的本质是函数&#xff0c…

java登录界面命令_Java命令行界面(第16部分):JArgp

java登录界面命令这篇文章中介绍的基于Java的命令行参数处理库是IBM developerWorks文章Java编程动态性&#xff0c;第3部分&#xff0c;应用的反射 &#xff08;第2003 部分&#xff0c;此归档文章于2016年“归档”&#xff0c;但仍可通过PDF下载 &#xff09;的特色库。 。 该…

QPW 用户签到日志表(tf_user_signin_log)

用户签到日志表 CREATE TABLE tf_user_signin_log (signin_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 签到ID,user_id bigint(20) DEFAULT NULL COMMENT 用户ID,insert_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间,update_time timestamp NOT …

tnsnames.ora配置未生效_1分钟了解网络交换机的6种命令配置模式

我们在配置交换机的时候首先要了解的就是交换机命令模式&#xff0c;小编用Cisco思科交换机为例带大家了解交换机的6种配置模式。Cisco IOS提供了用户EXEC模式和特权EXEC模式两种基本的命令执行级别&#xff0c;同时还提供了全局配置、接口配置、Line配置和vlan数据库配置等多种…

dockerfile mysql例子_docker-compose 实用示例

简单来说, docker compose就是一键启动/关闭多个容器的工具, 它能够帮你解决容器之间依赖的问题, 哪个先启动, 依赖哪个容器等.当开发的系统越来越复杂, 开发环境和部署都没那么简单的时候, 可以试试docker compose.下面会把我实际经验中的一个例子简化出来&#xff0c;一步步教…

QPW 操作日志表(tl_operate_log)

操作日志表 前端每次调后端服务时&#xff0c;会往这张表插入一条记录&#xff0c;作为接口调用日志&#xff0c;出现 bug 时可以查询这张表追溯问题。 CREATE TABLE tl_operate_log (operate_log_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 操作ID,user_id bigint(20) …

java 线程中创建线程_如何在Java 8中创建线程安全的ConcurrentHashSet?

java 线程中创建线程在JDK 8之前&#xff0c;还没有办法在Java中创建大型的线程安全的ConcurrentHashSet。 java.util.concurrent包甚至没有一个名为ConcurrentHashSet的类&#xff0c;但是从JDK 8开始&#xff0c;您可以使用新添加的keySet&#xff08;默认值&#xff09;和ne…

docker 删除所有镜像_关于 Docker 镜像的操作,看完这篇就够啦 !(下)| 文末福利...

紧接着上篇《关于 Docker 镜像的操作&#xff0c;看完这篇就够啦 !(上)》&#xff0c;奉上下篇 &#xff01;&#xff01;&#xff01;镜像作为 Docker 三大核心概念中最重要的一个关键词&#xff0c;它有很多操作&#xff0c;是您想学习容器技术不得不掌握的。本文将带您一步一…

排序出错Java_使用picard排序去重出错

命令&#xff1a;java -jar/public/home/nieyg/biosoft/package/picard-tools-1.124/picard.jar SortSam \I/public/home/nieyg/atacseq/data/Rawdata/align/LDN-D2-1.raw.bam\OLDN-D2-1.sort.bam \SORT_ORDERcoordinate错误信息&#xff1a;Exception in thread "main&qu…

移动端设备标识码/设备唯一标识码/设备唯一性

文章目录名词释义Android 设备标识码iOS设备标识码总结名词释义 Device ID&#xff1a;设备ID。 IMEI&#xff1a;International Mobile Equipment Identity&#xff0c;国际移动设备身份码的缩写。是由15位数字组成的“电子串号”&#xff0c;它与每台手机一一对应&#xff0…

python与access选哪个_从Python连接到Access

I want to be connected to a database Boreas (Access) from Python. How to be connected from Python to Access database Northwind? 解决方案 Here are 2 ways, with COM dispatch and with odbc. You will need the pywin32 extensions and/or pyodbc to use these meth…

aws实例启动失败_AWS:启动安装了APOC的Neo4j实例

aws实例启动失败安装Neo4j之后&#xff0c;我要做的第一件事就是安装APOC库 &#xff0c;但是我发现在AWS上旋转服务器时这是一个手动过程&#xff0c;所以我想简化一下。 已经有一个Neo4j AMI可以安装Neo4j 3.2.0 &#xff0c;我的同事Michael指出&#xff0c;我们可以通过编…

设备唯一标识/设备码/设备标识码

文章目录一、MAC地址二、IMEI三、MEIDMEID 和 IMEI 用途的区别四、序列号&#xff08;一&#xff09;苹果手机序列号&#xff08;二&#xff09;华为手机序列号一、MAC地址 MAC地址&#xff08;英语&#xff1a;Media Access Control Address&#xff09;&#xff0c;直译为媒…

QPW 点评表(tf_appraise)

点评表 CREATE TABLE tf_appraise (appraise_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 点评ID,company_id bigint(20) DEFAULT NULL COMMENT 企业ID,user_id bigint(20) DEFAULT NULL COMMENT 用户ID,avg_score decimal(4,2) DEFAULT 0.00 COMMENT 综合评分,contributi…

python多次输入数据_如何用python3输出重复的数据?

面对很多重复的数据&#xff0c;如果人工一个个处理起来会很麻烦&#xff0c;而且要浪费大量的时间和精力。之前就python处理数据是简单便捷的&#xff0c;有没有一种方法能把重复的数据输出出来&#xff0c;这样就一目了然了。相信有一些小伙伴也跟小编有同样的困扰&#xff0…

java magic number_避免JDBC查询中的CheckStyle magic number错误

我正在上课一个小组项目,我们正在尝试CheckStyle.我对Java非常满意,但从未触及到JDBC或在此之前完成任何数据库工作.我想知道如果有一个优雅的方式来避免在准备语句电话中出现错误的数字错误,请考虑&#xff1a;preparedStatement connect.prepareStatement("INSERT INTO…

java登录界面命令_Java命令行界面(第18部分):JCLAP

java登录界面命令Giles Winstanley的JCLAP &#xff08; Java命令行参数解析器 &#xff09;是基于Java的命令行处理库的系列文章中介绍的第18个库。 这篇文章的示例基于JCLAP 1.4 &#xff0c;它需要Java 8 。 JCLAP主页上指出&#xff1a;“ JCLAP帮助Java开发人员为其应用程…

MySQL命令之show用法详解

文章目录参考示例&#xff08;一&#xff09;查看表字段详情&#xff08;二&#xff09;显示某个表的创建语句&#xff08;三&#xff09;显示所有数据库的名称&#xff08;四&#xff09;查看已选数据库中的所有表&#xff08;五&#xff09;查看某个数据库的创建语句&#xf…