当前位置: 首页 > news >正文

万字解析设计模式之责任链模式、状态模式

目录

一、责任链模式

1.1概述

1.2结构

1.3实现

1.4 优缺点

1.5应用场景

1.6源码解析

二、状态模式

2.1概述

2.2结构

2.3实现

2.4优缺点

2.5应用场景 

三、责任链模式实验

任务描述

实现方式

编程要求

测试说明

四、状态模式实验

任务描述

实现方式

编程要求

测试说明


一、责任链模式

1.1概述

为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花”游戏等。

1.2结构

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
  • 请求(Request):包含需要进行处理的数据。

  • 响应(Response):包含处理结果。

1.3实现

现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。

请求(Request)

package com.yanyu.responsibilily;public class LeaveRequest {private String name;//姓名private int num;//请假天数private String content;//请假内容public LeaveRequest(String name, int num, String content) {this.name = name;this.num = num;this.content = content;}public String getName() {return name;}public int getNum() {return num;}public String getContent() {return content;}
}

抽象处理者(Handler)角色

package com.yanyu.responsibilily;//处理者抽象类
public abstract class Handler {protected final static int NUM_ONE = 1;protected final static int NUM_THREE = 3;protected final static int NUM_SEVEN = 7;//该领导处理的请假天数区间private int numStart;private int numEnd;//领导上面还有领导private Handler nextHandler;//设置请假天数范围 上不封顶public Handler(int numStart) {this.numStart = numStart;}//设置请假天数范围public Handler(int numStart, int numEnd) {this.numStart = numStart;this.numEnd = numEnd;}//设置上级领导public void setNextHandler(Handler nextHandler){this.nextHandler = nextHandler;}//各级领导处理请假条方法protected abstract void handleLeave(LeaveRequest leave);//提交请假条public final void submit(LeaveRequest leave){if(0 == this.numStart){return;}//如果请假天数达到该领导者的处理要求if(leave.getNum() >= this.numStart){this.handleLeave(leave);//如果还有上级 并且请假天数超过了当前领导的处理范围if(null != this.nextHandler && leave.getNum() > numEnd){this.nextHandler.submit(leave);//继续提交} else {System.out.println("流程结束");}}}}

具体处理者(Concrete Handler)角色

package com.yanyu.responsibilily;//小组长
public class GroupLeader extends Handler {public GroupLeader() {//小组长处理1-3天的请假super(Handler.NUM_ONE, Handler.NUM_THREE);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("小组长审批:同意。");}
}
package com.yanyu.responsibilily;//部门经理
public class Manager extends Handler {public Manager() {//部门经理处理3-7天的请假super(Handler.NUM_THREE, Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("部门经理审批:同意。");}
}
package com.yanyu.responsibilily;//总经理
public class GeneralManager extends Handler {public GeneralManager() {//部门经理处理7天以上的请假super(Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("总经理审批:同意。");}
}

客户端类

package com.yanyu.responsibilily;public class Client {public static void main(String[] args) {//请假条来一张LeaveRequest leave = new LeaveRequest("小花",5,"身体不适");//各位领导GroupLeader groupLeader = new GroupLeader();Manager manager = new Manager();GeneralManager generalManager = new GeneralManager();groupLeader.setNextHandler(manager);//小组长的领导是部门经理manager.setNextHandler(generalManager);//部门经理的领导是总经理//之所以在这里设置上级领导,是因为可以根据实际需求来更改设置,如果实战中上级领导人都是固定的,则可以移到领导实现类中。//提交申请groupLeader.submit(leave);}
}

1.4 优缺点

1,优点:

  • 降低了对象之间的耦合度

    该模式降低了请求发送者和接收者的耦合度。

  • 增强了系统的可扩展性

    可以根据需要增加新的请求处理类,满足开闭原则。

  • 增强了给对象指派职责的灵活性

    当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。

  • 责任链简化了对象之间的连接

    一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

  • 责任分担

    每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

2,缺点:

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

1.5应用场景

  1. 当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。该模式能将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求。

  2. 当必须按顺序执行多个处理者时, 可以使用该模式。无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者。

  3. 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序。

1.6源码解析

在javaWeb应用开发中,FilterChain是职责链(过滤器)模式的典型应用,以下是Filter的模拟实现分析:

模拟web请求Request以及web响应Response


public interface Request{}
​
public interface Response{}

模拟web过滤器Filter

 public interface Filter {public void doFilter(Request req,Response res,FilterChain c);}

模拟实现具体过滤器


public class FirstFilter implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {
​System.out.println("过滤器1 前置处理");
​// 先执行所有request再倒序执行所有responsechain.doFilter(request, response);
​System.out.println("过滤器1 后置处理");}
}
​
public class SecondFilter  implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {
​System.out.println("过滤器2 前置处理");
​// 先执行所有request再倒序执行所有responsechain.doFilter(request, response);
​System.out.println("过滤器2 后置处理");}
}

模拟实现过滤器链FilterChain

public class FilterChain {
​private List<Filter> filters = new ArrayList<Filter>();
​private int index = 0;
​// 链式调用public FilterChain addFilter(Filter filter) {this.filters.add(filter);return this;}
​public void doFilter(Request request, Response response) {if (index == filters.size()) {return;}Filter filter = filters.get(index);index++;filter.doFilter(request, response, this);}
}

测试类


public class Client {public static void main(String[] args) {Request  req = null;Response res = null ;
​FilterChain filterChain = new FilterChain();filterChain.addFilter(new FirstFilter()).addFilter(new SecondFilter());filterChain.doFilter(req,res);}
}

二、状态模式

2.1概述

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

 【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。

问题分析:

  • 使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。
  • 扩展性很差。如果新加了断电的状态,我们需要修改上面判断逻辑

2.2结构

状态模式包含以下主要角色。

  • 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为。

2.3实现

环境(Context)角色

package com.yanyu.State;//环境角色
public class Context {//定义出所有的电梯状态public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行//定义一个当前电梯状态private LiftState liftState;public LiftState getLiftState() {return this.liftState;}public void setLiftState(LiftState liftState) {//当前环境改变this.liftState = liftState;//把当前的环境通知到各个实现类中this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}
}

抽象状态(State)角色

package com.yanyu.State;//抽象状态类
public abstract class LiftState {//定义一个环境角色,也就是封装状态的变化引起的功能变化protected Context context;public void setContext(Context context) {this.context = context;}//电梯开门动作public abstract void open();//电梯关门动作public abstract void close();//电梯运行动作public abstract void run();//电梯停止动作public abstract void stop();
}

具体状态(Concrete State)角色

package com.yanyu.State;//开启状态
public class OpenningState extends LiftState {//开启当然可以关闭了,我就想测试一下电梯门开关功能@Overridepublic void open() {System.out.println("电梯门开启...");}//关闭电梯门@Overridepublic void close() {//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//电梯门不能开着就跑,这里什么也不做@Overridepublic void run() {//do nothing}//开门状态已经是停止的了@Overridepublic void stop() {//do nothing}
}
package com.yanyu.State;// 关闭状态类,继承自电梯状态类
public class ClosingState extends LiftState {@Override// 电梯门关闭,这是关闭状态要实现的动作public void close() {System.out.println("电梯门关闭...");}// 电梯门关了再打开,逗你玩呢,那这个允许呀@Overridepublic void open() {// 设置电梯状态为开启状态super.context.setLiftState(Context.openningState);// 调用开启状态的open方法super.context.open();}// 电梯门关了就跑,这是再正常不过了@Overridepublic void run() {// 设置电梯状态为运行状态super.context.setLiftState(Context.runningState);// 调用运行状态的run方法super.context.run();}// 电梯门关着,我就不按楼层@Overridepublic void stop() {// 设置电梯状态为停止状态super.context.setLiftState(Context.stoppingState);// 调用停止状态的stop方法super.context.stop();}
}
package com.yanyu.State;// 运行状态类,继承自电梯状态类
public class RunningState extends LiftState {// 运行的时候开电梯门?你疯了!电梯不会给你开的@Overridepublic void open() {// do nothing}// 电梯门关闭?这是肯定了@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行// do nothing}// 这是在运行状态下要实现的方法@Overridepublic void run() {System.out.println("电梯正在运行...");}// 这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了@Overridepublic void stop() {// 设置电梯状态为停止状态super.context.setLiftState(Context.stoppingState);// 调用停止状态的stop方法super.context.stop();}
}
package com.yanyu.State;// 停止状态类,继承自电梯状态类
public class StoppingState extends LiftState {// 停止状态,开门,那是要的!@Overridepublic void open() {// 状态修改为开启状态super.context.setLiftState(Context.openningState);// 动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().open();}@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行// 状态修改为关闭状态super.context.setLiftState(Context.closeingState);// 动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}// 停止状态再跑起来,正常的很@Overridepublic void run() {// 状态修改为运行状态super.context.setLiftState(Context.runningState);// 动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().run();}// 停止状态是怎么发生的呢?当然是停止方法执行了@Overridepublic void stop() {System.out.println("电梯停止了...");}
}

客户端类

//测试类
public class Client {public static void main(String[] args) {Context context = new Context();context.setLiftState(new ClosingState());
​context.open();context.close();context.run();context.stop();}
}

2.4优缺点

1,优点:

  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

2,缺点:

  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对"开闭原则"的支持并不太好。

2.5应用场景 

  • 如果对象需要根据自身当前状态进行不同行为, 同时状态的数量非常多且与状态相关的代码会频繁变更的话, 可使用状态模式;

  • 如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时,可使用该模式;

  • 当相似状态和基于条件的状态机转换中存在许多重复代码时, 可使用状态模式。

三、责任链模式实验

任务描述

该场景描述的是一个代检产品在流水线上检查,产品有两个属性,长度和宽度,流水线上的处理节点也有两个,即长度检验器和宽度检验器。

本关任务:用责任链模式实现产品的检测,先检测宽度(50<width<100 合格),再检测高度(10<height<50 合格),宽度和高度都合格则产品合格,否则产品不合格。

实现方式

  1. 声明处理者接口并描述请求处理方法的签名。确定客户端如何将请求数据传递给方法。 最灵活的方式是将请求转换为对象, 然后将其以参数的形式传递给处理函数;

  2. 为了在具体处理者中消除重复的样本代码, 你可以根据处理者接口创建抽象处理者基类。该类需要有一个成员变量来存储指向链上下个处理者的引用。 你可以将其设置为不可变类。 但如果你打算在运行时对链进行改变, 则需要定义一个设定方法来修改引用成员变量的值。为了使用方便, 你还可以实现处理方法的默认行为。 如果还有剩余对象, 该方法会将请求传递给下个对象。 具体处理者还能够通过调用父对象的方法来使用这一行为;

  3. 依次创建具体处理者子类并实现其处理方法。 每个处理者在接收到请求后都必须做出两个决定:是否自行处理这个请求。是否将该请求沿着链进行传递;

  4. 客户端可以自行组装链, 或者从其他对象处获得预先组装好的链。 在后一种情况下, 你必须实现工厂类以根据配置或环境设置来创建链;

  5. 客户端可以触发链中的任意处理者, 而不仅仅是第一个。 请求将通过链进行传递, 直至某个处理者拒绝继续传递, 或者请求到达链尾;

  6. 由于链的动态性, 客户端需要准备好处理以下情况:

    • 链中可能只有单个链接;
    • 部分请求可能无法到达链尾;
    • 其他请求可能直到链尾都未被处理。

编程要求

根据提示,在右侧编辑器 Begin-End 内补充 “HeightCheckMiddleware.java” 和 “WidthCheckMiddleware.java” 的代码。

测试说明

平台会对你编写的代码进行测试:

测试输入:60 20 预期输出: 产品宽度检验通过 产品高度检验通过 产品最终检验合格!

测试输入:49 20 预期输出: 产品宽度未检验通过 产品最终检验不合格!

请求

package step1;import java.util.HashMap;
import java.util.Map;// 产品类,包含宽度和高度属性
public class Product {private Middleware middleware; // 中间件对象private int width; // 宽度private int height; // 高度// 构造方法,初始化宽度和高度public Product(int width,int height) {this.width =width;this.height = height;}// 设置中间件对象public void setMiddleware(Middleware middleware) {this.middleware = middleware;}// 获取高度public int getHeight() {return height;}// 获取宽度public int getWidth() {return width;}// 执行产品检验,调用中间件的check方法进行检验public boolean doProcess() {if (middleware.check(width,height)) {System.out.println("产品最终检验合格!");return true;}System.out.println("产品最终检验不合格!");return false;}
}

抽象处理者(Handler)角色

package step1;public abstract class Middleware {private Middleware next;/* Builds chains of middleware objects.*/public static Middleware link(Middleware first, Middleware... chain) {Middleware head = first;for (Middleware nextInChain: chain) {head.next = nextInChain;head = nextInChain;}return first;}/*** 在具体的节点中去实现check.*/public abstract boolean check(int width, int height);/*** Runs check on the next object in chain or ends traversing if we're in* last object in chain.*/protected boolean checkNext(int width, int height) {if (next == null) {return true;}return next.check(width, height);}
}

具体处理者(Handler)角色

package step1;public class HeightCheckMiddleware extends Middleware{public boolean check(int width, int height) {/*产品高度检验通过 产品高度未检验通过*//********** Begin *********/if (height > 10 && height < 50) {System.out.println("产品高度检验通过");return checkNext(width, height);} else {System.out.println("产品高度未检验通过");return false;}/********** End *********/}
}
package step1;public class WidthCheckMiddleware extends Middleware {public boolean check(int width, int height) {/*产品宽度未检验通过   产品宽度检验通过*//********** Begin *********/if (width > 50 && width < 100) {System.out.println("产品宽度检验通过");return checkNext(width, height);} else {System.out.println("产品宽度未检验通过");return false;}/********** End *********/}
}

客户端类

package step1;import java.io.IOException;
import java.util.Scanner;public class Client {public static void main(String[] args) throws IOException {// 创建一个扫描器对象,用于读取用户输入的宽度和高度Scanner scanner = new Scanner(System.in);int width = scanner.nextInt(); // 读取用户输入的宽度int height = scanner.nextInt(); // 读取用户输入的高度Product product = new Product(width,height); // 创建一个产品对象,传入宽度和高度// 构建检测链Middleware middleware = Middleware.link(new WidthCheckMiddleware(), // 创建一个宽度检查中间件对象new HeightCheckMiddleware() // 创建一个高度检查中间件对象);// 将检测链的首节点设置为产品对象的中间件product.setMiddleware(middleware);// 启动产品对象的检测链product.doProcess();}
}

四、状态模式实验

任务描述

请你为某商城设计一个会员程序,要求如下:

  • 商城将顾客分为普通、黄金、VIP 三个等级,普通会员消费时没有折扣,黄金会员95折,VIP 会员85折;

  • 积分规则:按单笔消费金额等额取整法,例如客户消费1元积1分,消费1.5元也是积一分,消费2元则积2分;

  • 刚加入的顾客被归入普通会员,要求填入姓名;当顾客积分大于等于500时自动升级为黄金会员,下次享受黄金会员待遇;当积分大于等于2000时自动升级为 VIP 会员,下次起享受 VIP 会员待遇。注意:会员升级过程不能跳级。

实现方式

  1. 确定哪些类是上下文。 它可能是包含依赖于状态的代码的已有类; 如果特定于状态的代码分散在多个类中, 那么它可能是一个新的类;

  2. 声明状态接口。 虽然你可能会需要完全复制上下文中声明的所有方法, 但最好是仅把关注点放在那些可能包含特定于状态的行为的方法上;

  3. 为每个实际状态创建一个继承于状态接口的类。 然后检查上下文中的方法并将与特定状态相关的所有代码抽取到新建的类中。在将代码移动到状态类的过程中, 你可能会发现它依赖于上下文中的一些私有成员。 你可以采用以下几种变通方式:

    • 将这些成员变量或方法设为公有;

    • 将需要抽取的上下文行为更改为上下文中的公有方法, 然后在状态类中调用。 这种方式简陋却便捷, 你可以稍后再对其进行修补;

    • 将状态类嵌套在上下文类中。 这种方式需要你所使用的编程语言支持嵌套类。

  1. 在上下文类中添加一个状态接口类型的引用成员变量, 以及一个用于修改该成员变量值的公有设置器;

  2. 再次检查上下文中的方法, 将空的条件语句替换为相应的状态对象方法;

  3. 为切换上下文状态, 你需要创建某个状态类实例并将其传递给上下文。 你可以在上下文、 各种状态或客户端中完成这项工作。 无论在何处完成这项工作, 该类都将依赖于其所实例化的具体类。

编程要求

根据提示,在右侧编辑器 Begin-End 内补全代码,需要补充代码的文件如下:

  • AbstractState.java
  • CommonState.java
  • GoldState.java
  • clubAccount.java

测试说明

输入第一行表示顾客姓名,第二行给出一个正整数 n(n⩽10)表示消费次数。随后 n 行,每行给出1个实数(消费金额)。输出 n 行结果,格式为 XX 本次消费金额为 XX,折扣后为 XX

测试输入: 张三 3 612.0 1621.0 100.0 预期输出: 张三注册成功 普通会员本次消费金额:612.0,折扣后:612.0,当前积分:612 黄金会员本次消费金额:1621.0,折扣后:1539.9,当前积分:2151 VIP会员本次消费金额:100.0,折扣后:85.0,当前积分:2236

环境(Context)角色

package step1;public class clubAccount {private String name; // 姓名private AbstractState state; // 当前状态public clubAccount(String name) {this.name = name;// 初始化账户状态为普通状态this.state = new CommonState(this);System.out.println(this.name + "注册成功!");}// 设置账户状态public void setState(AbstractState state) {this.state = state;}// 消费public void Consume(double money) {/********** Begin *********/// 调用当前状态的消费方法state.Consume(money);/********** End *********/}
}

这里的new CommonState(this)表示创建一个新的状态对象,并将当前对象(即this)作为参数传递给CommonState的构造函数

 抽象状态(State)角色

package step1;public abstract class AbstractState {protected  clubAccount account;//账户protected double discount;//折扣比例protected int userPoints;//积分protected String stateName;//状态名public void Consume(double money){/********** Begin *********/userPoints += (int)(money*discount);checkState();/********** End *********////现金消费System.out.println(stateName+"本次消费金额:"+money+",折扣后:"+String.format("%.1f",money*discount)+",当前积分:"+userPoints);}///若有积分抵现金或领取礼物则需要修改checkState原型,请自由扩展积分消费函数public abstract void checkState();
}

 具体状态(State)角色

package step1;// 定义一个名为CommonState的类,继承自AbstractState类
public class CommonState extends AbstractState {// 构造方法1:接收一个AbstractState类型的参数statepublic CommonState(AbstractState state) {// 将传入的state对象的userPoints属性赋值给当前对象的userPoints属性this.userPoints = state.userPoints;// 设置当前对象的状态名称为"普通会员"this.stateName = "普通会员";// 将传入的state对象的account属性赋值给当前对象的account属性this.account = state.account;// 设置当前对象的折扣为1this.discount = 1;}// 构造方法2:接收一个clubAccount类型的参数accountpublic CommonState(clubAccount account) {// 将传入的account对象的account属性赋值给当前对象的account属性this.account = account;// 设置当前对象的用户积分为0this.userPoints = 0;// 设置当前对象的状态名称为"普通会员"this.stateName = "普通会员";// 设置当前对象的折扣为1this.discount = 1;}// 重写checkState方法@Overridepublic void checkState() {/********** Begin *********/// 如果用户积分大于等于2000,将账户状态设置为VIPStateif (userPoints >= 2000) {account.setState(new VIPState(this));}// 如果用户积分大于等于500,将账户状态设置为GoldStateelse if (userPoints >= 500) {account.setState(new GoldState(this));}/********** End *********/}
}
package step1;public class GoldState extends AbstractState{public GoldState(AbstractState state){this.userPoints=state.userPoints;this.stateName="黄金会员";this.account= state.account;this.discount=0.95;}@Overridepublic void checkState() {/********** Begin *********/if (userPoints >= 2000) {account.setState(new VIPState(this));}/********** End *********/}
}
package step1;public class VIPState extends AbstractState{public VIPState(AbstractState state){this.userPoints=state.userPoints;this.stateName="VIP会员";this.account= state.account;this.discount=0.85;}@Overridepublic void checkState() {}
}

客户端类 

package step1;import java.util.Scanner;public class Client {public static void main(String[] args) {// 创建一个扫描器对象,用于读取用户输入Scanner scanner = new Scanner(System.in);// 读取用户输入的姓名String name = scanner.next();// 根据姓名创建一个俱乐部账户对象clubAccount account = new clubAccount(name);// 读取用户输入的消费次数int n = scanner.nextInt();// 定义一个变量用于存储每次消费的金额float money;// 循环n次,每次读取用户输入的消费金额并调用Consume方法进行消费操作for (int i = 0; i < n; i++) {money = scanner.nextFloat();account.Consume(money);}}
}

相关文章:

万字解析设计模式之责任链模式、状态模式

目录 一、责任链模式 1.1概述 1.2结构 1.3实现 1.4 优缺点 1.5应用场景 1.6源码解析 二、状态模式 2.1概述 2.2结构 2.3实现 2.4优缺点 2.5应用场景 三、责任链模式实验 任务描述 实现方式 编程要求 测试说明 四、状态模式实验 任务描述 实现方式 编程要…...

二十三种设计模式全面解析-深入探讨状态模式的高级应用技术:释放对象行为的无限可能

在软件开发中&#xff0c;状态管理是一个常见的挑战。当对象的行为随着内部状态的变化而变化时&#xff0c;有效地管理对象的状态和相应的行为变得至关重要。在这方面&#xff0c;状态模式提供了一种优雅而灵活的解决方案。它允许对象在运行时根据内部状态的改变而改变其行为&a…...

论文笔记--Toolformer: Language Models Can Teach Themselves to Use Tools

论文笔记--Toolformer: Language Models Can Teach Themselves to Use Tools 1. 文章简介2. 文章概括3 文章重点技术3.1 Toolformer3.2 APIs 4. 文章亮点5. 原文传送门 1. 文章简介 标题&#xff1a;Toolformer: Language Models Can Teach Themselves to Use Tools作者&#…...

stm32实现0.96oled图片显示,菜单功能

stm32实现0.96oled图片显示&#xff0c;菜单功能 功能展示简介代码介绍oled.coled.holedfont.h&#xff08;字库文件&#xff09;main函数 代码思路讲解 本期内容&#xff0c;我们将学习0.96寸oled的进阶使用&#xff0c;展示图片&#xff0c;实现菜单切换等功能&#xff0c;关…...

sqlite外键约束 保证数据一致性

1. 外键约束 在SQLite中&#xff0c;可以通过使用外键&#xff08;Foreign Key&#xff09;约束和CASCADE选项来实现通过外键删除相关信息。 CASCADE选项是指在主键表中删除记录时&#xff0c;相应的外键表中的相关记录也将被自动删除。 -- 创建主键表 CREATE TABLE Persons…...

Vue轻松入门,附带学习笔记和相关案例

目录 案例 一Vue基础 什么是Vue&#xff1f; 补充&#xff1a;mvvm框架 mvvm的组成 详解 Vue的使用方法 1.直接下载并引入 2.通过 CDN 使用 Vue 3.通过npm安装 4.使用Vue CLI创建项目 二插值表达式 什么是插值表达式&#xff1f; 插值表达式的缺点 解决方法 …...

【青蛙跳台阶问题 —— (三种算法)】

青蛙跳台阶问题 —— (三种算法&#xff09; 一.题目介绍1.1.题目1.2.图示 二.解题思路三.题解及其相关算法3.1.递归分治法3.2.动态规划算法&#xff08;Dynamic Programming&#xff09;3.3.斐波那契数列法 四.注意细节 一.题目介绍 1.1.题目 一只青蛙一次可以跳上1级台阶&am…...

联想yoga AMD处理器 转接头无法电量外接显示器

第一次买AMD的处理器&#xff0c;当时就是为了yogaAMD这款的接口要比英特尔的接口多&#xff0c;没想到AMD处理器真的问题多。经常蓝屏不说&#xff0c;偶尔还点不亮外接显示器。遇到这种问题&#xff0c;不是什么驱动问题&#xff0c;可能你按照网上各种方法打开设备管理器→显…...

OSG粒子系统与阴影 - ​​​​​​​阴影shadow(7)

OSG阴影 在虚拟现实仿真中&#xff0c;为了真实地模拟自然效果&#xff0c;阴影效果是不可缺少的&#xff0c;它对一个场景的真实性是非常重要的。在游戏或仿真中&#xff0c;一个高效的阴影往往能够提供非常强悍的视觉真实感。 osgShadow库 在OSG中专门定义了一个名字空间osg…...

vue3项目中使用富文本编辑器

前言 适配 Vue3 的富文本插件不多&#xff0c;我看了很多插件官网&#xff0c;也有很多写的非常棒的&#xff0c;有UI非常优雅让人耳目一新的&#xff0c;也有功能非常全面的。 如&#xff1a; Quill&#xff0c;简单易用&#xff0c;功能全面。editorjs&#xff0c;UI极其优…...

Java EE 进程线程

JavaEE 进程&线程 文章目录 JavaEE 进程&线程1. 进程1.1 概念1.2 进程管理1.3 PCB (Process Control Block) 2. 线程2.1 概念2.1 线程与进程的区别2.3 创建线程 1. 进程 1.1 概念 什么是进程&#xff1f; 进程是操作系统对一个正在执行的程序的一种抽象 我们可以打开…...

GPT写SQL的模版

表&#xff1a;profit_loss_sum_m_snapshot 计算字段&#xff1a;成本cost_whole求和&#xff0c;收入income_whole求和&#xff0c;收入求和-成本求和&#xff0c;成本目标cost_target求和&#xff0c;收入求和-成本目标求和 条件&#xff1a;日期statis_date在2023-11-01&…...

蓝桥杯官网练习题(平均)

问题描述 有一个长度为 n 的数组&#xff08; n 是 10 的倍数&#xff09;&#xff0c;每个数 ai 都是区间 [0,9] 中的整数。小明发现数组里每种数出现的次数不太平均&#xff0c;而更改第 i 个数的代价为 bi&#xff0c;他想更改若干个数的值使得这 10 种数出现的次数相等…...

【无标题】动手学深度学习_现代神经网络_未完

这里写目录标题 深度学习之前的网络 AlexNetAlexNet得到了竞赛冠军AlexNet架构Alex net更多细节数据增强 VGGNiN知识补充flop暂退法 drop_out 深度学习之前的网络 1、核方法 机器学习 SVM现在还是很广泛的使用&#xff0c;因为对调参的需求不那么大&#xff0c;对调参不太敏感…...

Java王者荣耀

GameFrame 图片 package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList;import javax.soun…...

【理解ARM架构】操作寄存器实现UART | 段的概念 | IDE背后的命令

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《理解ARM架构》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f360;操作寄存器实现UART&#x1f35f;UART原理&#x1f35f;编程 &#x1f360;…...

python 左值查找 右值查找

左值查找 在一组数据中查找出 数字x 在这组数据中第一次出现的索引并输出&#xff0c;没有找到则输出-1查找方式&#xff1a;二分查找 数据前提&#xff1a;一组数据要有序一组数据&#xff1a; arr [2, 3, 3, 3, 5, 7, 9, 11, 13, 15, 17]测试&#xff1a; 示例1&#xff…...

机器学习之自监督学习(四)MoCo系列翻译与总结(二)

MoCo中相关工作的对比分析 去噪自动编码器&#xff08;Denoising Autoencoder&#xff09;是一种用于学习数据表示的神经网络模型。它的主要目标是通过去除输入数据中的噪声&#xff0c;学习到输入数据的有用表示&#xff0c;从而提高模型对干净数据的鲁棒性。下面是对去噪自动…...

元宇宙企业3d数字展厅轻松低本搭建更全面、多元、趣味化的展览

对所有企业来说&#xff0c;拥有一个3D线上展厅是互联网营销必不可少的部分&#xff0c;但是3D线上展厅定制周期长费用高&#xff0c;让很多企业公司望而却步&#xff0c;web3d开发公司制作的3D线上企业展厅制作平台备导览地图、语音解说、交互热点、全景漫游、自主行走、链接跳…...

华为OD机试真题-开源项目热榜-2023年OD统一考试(C卷)

题目描述: 某个开源社区希望将最近热度比较高的开源项目出一个榜单,推荐给社区里面的开发者。对于每个开源项目,开发者可以进行关注(watch)、收藏(star)、fork、提issue、提交合并请求(MR)等。 数据库里面统计了每个开源项目关注、收藏、fork、issue、MR的数量,开源项目的热…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …...