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

目录

一、责任链模式

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);
    }

    @Override
    protected 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);
    }

    @Override
    protected 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);
    }

    @Override
    protected 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 {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
​
        System.out.println("过滤器1 前置处理");
​
        // 先执行所有request再倒序执行所有response
        chain.doFilter(request, response);
​
        System.out.println("过滤器1 后置处理");
    }
}
​
public class SecondFilter  implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
​
        System.out.println("过滤器2 前置处理");
​
        // 先执行所有request再倒序执行所有response
        chain.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 {

    //开启当然可以关闭了,我就想测试一下电梯门开关功能
    @Override
    public void open() {
        System.out.println("电梯门开启...");
    }

    //关闭电梯门
    @Override
    public void close() {
        //状态修改
        super.context.setLiftState(Context.closeingState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().close();
    }

    //电梯门不能开着就跑,这里什么也不做
    @Override
    public void run() {
        //do nothing
    }

    //开门状态已经是停止的了
    @Override
    public void stop() {
        //do nothing
    }
}
package com.yanyu.State;

// 关闭状态类,继承自电梯状态类
public class ClosingState extends LiftState {
    @Override
    // 电梯门关闭,这是关闭状态要实现的动作
    public void close() {
        System.out.println("电梯门关闭...");
    }

    // 电梯门关了再打开,逗你玩呢,那这个允许呀
    @Override
    public void open() {
        // 设置电梯状态为开启状态
        super.context.setLiftState(Context.openningState);
        // 调用开启状态的open方法
        super.context.open();
    }

    // 电梯门关了就跑,这是再正常不过了
    @Override
    public void run() {
        // 设置电梯状态为运行状态
        super.context.setLiftState(Context.runningState);
        // 调用运行状态的run方法
        super.context.run();
    }

    // 电梯门关着,我就不按楼层
    @Override
    public void stop() {
        // 设置电梯状态为停止状态
        super.context.setLiftState(Context.stoppingState);
        // 调用停止状态的stop方法
        super.context.stop();
    }
}
package com.yanyu.State;

// 运行状态类,继承自电梯状态类
public class RunningState extends LiftState {

    // 运行的时候开电梯门?你疯了!电梯不会给你开的
    @Override
    public void open() {
        // do nothing
    }

    // 电梯门关闭?这是肯定了
    @Override
    public void close() {//虽然可以关门,但这个动作不归我执行
        // do nothing
    }

    // 这是在运行状态下要实现的方法
    @Override
    public void run() {
        System.out.println("电梯正在运行...");
    }

    // 这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
    @Override
    public void stop() {
        // 设置电梯状态为停止状态
        super.context.setLiftState(Context.stoppingState);
        // 调用停止状态的stop方法
        super.context.stop();
    }
}
package com.yanyu.State;

// 停止状态类,继承自电梯状态类
public class StoppingState extends LiftState {

    // 停止状态,开门,那是要的!
    @Override
    public void open() {
        // 状态修改为开启状态
        super.context.setLiftState(Context.openningState);
        // 动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().open();
    }

    @Override
    public void close() {//虽然可以关门,但这个动作不归我执行
        // 状态修改为关闭状态
        super.context.setLiftState(Context.closeingState);
        // 动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().close();
    }

    // 停止状态再跑起来,正常的很
    @Override
    public void run() {
        // 状态修改为运行状态
        super.context.setLiftState(Context.runningState);
        // 动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().run();
    }

    // 停止状态是怎么发生的呢?当然是停止方法执行了
    @Override
    public 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类型的参数state
    public CommonState(AbstractState state) {
        // 将传入的state对象的userPoints属性赋值给当前对象的userPoints属性
        this.userPoints = state.userPoints;
        // 设置当前对象的状态名称为"普通会员"
        this.stateName = "普通会员";
        // 将传入的state对象的account属性赋值给当前对象的account属性
        this.account = state.account;
        // 设置当前对象的折扣为1
        this.discount = 1;
    }

    // 构造方法2:接收一个clubAccount类型的参数account
    public CommonState(clubAccount account) {
        // 将传入的account对象的account属性赋值给当前对象的account属性
        this.account = account;
        // 设置当前对象的用户积分为0
        this.userPoints = 0;
        // 设置当前对象的状态名称为"普通会员"
        this.stateName = "普通会员";
        // 设置当前对象的折扣为1
        this.discount = 1;
    }

    // 重写checkState方法
    @Override
    public void checkState() {
        /********** Begin *********/
        // 如果用户积分大于等于2000,将账户状态设置为VIPState
        if (userPoints >= 2000) {
            account.setState(new VIPState(this));
        }
        // 如果用户积分大于等于500,将账户状态设置为GoldState
        else 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;
    }
    @Override
    public 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;
    }
    @Override
    public 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);
        }
    }
}

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

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

相关文章

什么是无监督学习

1 概况 1.1 定义 无监督学习&#xff08;Unsupervised Learning&#xff09;是机器学习的一种类型&#xff0c;它涉及从未标记的数据中发现隐藏的模式。与监督学习不同&#xff0c;无监督学习的数据没有显式的标签或已知的结果变量。其核心目的是探索数据的内在结构和关系。无…

使用Kibana让es集群形象起来

部署Elasticsearch集群详细步骤参考本人&#xff1a; https://blog.csdn.net/m0_59933574/article/details/134605073?spm1001.2014.3001.5502https://blog.csdn.net/m0_59933574/article/details/134605073?spm1001.2014.3001.5502 kibana部署 es集群设备 安装软件主机名…

76. 最小覆盖子串 (滑动窗口)

Problem: 76. 最小覆盖子串 文章目录 思路相似滑动窗口题目 :Code 题目 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 “” 。 注意&#xff1a; 对于 t 中重复字符&#xff0c;我…

宝塔面板安装搭建DiscuzQ论坛教程与小程序上架发布后的展示效果

DiscuzQ论坛小程序上架发布后的展示效果&#xff1a; 1、需要用到的环境&#xff1a; php7.2 mysql5.7或者MariaDB 10.2(我安装用的mysql8.0) php除了必要的一些扩展外&#xff0c;还需要启用readlink、symlink函数等&#xff0c;具体看官方说明&#xff0c;安装的时候也会提醒…

IDEA中JDK21控制台打印的中文乱码

IDEA中&#xff0c;使用的JDK21&#xff0c;控制台打印中文乱码&#xff0c;解决办法是重装了一下JDK。 我之前安装的版本是“jdk-21_windows-x64_bin.exe”&#xff0c;我配置了多个JDK环境&#xff0c;所以使用的是安装文件进行安装的。这次解决乱码问题&#xff0c;我重新安…

力软vue前端开发:使用params跳转传参404问题解决

问题描述 this.$router.push({ name: page, query: { id: 001 } }) // 根据路由名称 query 的方式跳转传参 使用query传参时&#xff0c;参数会拼接在链接后&#xff0c;点击搜索条件链接参数也还在。用户需要重新进入搜索页面。 所以&#xff0c;使用nameparams进行传参。参…

【Linux】vim-多模式的文本编辑器

本篇文章内容和干货较多&#xff0c;希望对大家有所帮助&#x1f44d; 目录 一、vim的介绍 1.1 vi 与 vim的概念1.2 Vim 和 Vi 的一些对比 二、vim 模式之间的切换 2.1 进入vim2.2 [正常模式]切换到[插入模式]2.3 [插入模式]切换至[正常模式]2.4 [正常模式]切换至[底行模式…

nodejs+vue+python+PHP+微信小程序-书吧租阅管理系统的设计与实现-安卓-计算机毕业设计

在当今高度发达的信息中&#xff0c;信息管理改革已成为一种更加广泛和全面的趋势。为确保中国经济的持续发展&#xff0c;信息时代日益更新&#xff0c;书吧租阅管理系统仍在蓬勃发展。同时&#xff0c;随着信息社会的快速发展&#xff0c;各种管理系统面临着越来越多的数据需…

spring-framework-5.2.25.RELEASE源码环境搭建

环境准备 spring-framework-5.2.25.RELEASEIntelliJ IDEA 2022.3.1java version “11.0.20” 2023-07-18 LTSGradle 5.6.4java version “1.8.0_301” 下载spring-framework-5.2.25.RELEASE源码 git clone https://gitee.com/QQ952051088/spring.git cd spring gradlew buil…

30系列显卡在ubuntu下不能满血运行的问题

之前发现在ubuntu下&#xff0c;我的3080只能跑115w最高&#xff0c;而这在win下是可以跑165w的。于是乎google了所有结果&#xff0c;无解… 现已经过去一年&#xff0c;显卡价格飞涨&#xff0c;无奈只能使用笔记本跑自己的代码了。结果发现nvidia推了Linux下的动态加速&…

jQuery_05 事件的绑定

jQuery可以给dom对象添加事件 在程序执行期间动态的处理事件 jQuery如何绑定事件呢&#xff1f; 1. $("选择器").事件名称(事件处理函数) $("选择器") &#xff1a; 选择0或者多个dom对象 给他们添加事件 事件名称&#xff1a;就是js中事件名称去掉on的部…

Linux 命令vim(编辑器)

(一)vim编辑器的介绍 vim是文件编辑器&#xff0c;是vi的升级版本&#xff0c;兼容vi的所有指令&#xff0c;同时做了优化和延伸。vim有多种模式&#xff0c;其中常用的模式有命令模式、插入模式、末行模式&#xff1a;。 (二)vim编辑器基本操作 1 进入vim编辑文件 1 vim …

河南省第五届“金盾信安杯”网络与数据安全大赛实操技能赛 部分wp(自己的一些思路和解析 )(主misc crypto )

芜湖 不评价 以下仅是自己的一些思路和解析 有什么问题或者建议随时都可以联系我 目录 题目一 来都来了 操作内容&#xff1a; flag值&#xff1a; 题目二 Honor 操作内容&#xff1a; flag值&#xff1a; 题目三 我看看谁还不会RSA 操作内容&#xff1a; flag值&a…

3. 内存单元

1位的内存单元 对于一个内存单元需要有:1个锁存器,数据输入,可写控制,是否读取(也是是否输出), 行和列(内存地址), 数据输出这几部分组成写入: 当行和列, 数据输入,可写全为1时则写入,(行 & 列 & 输入 & 可写)读出(输出): 当 行,列, 是否读取(也是是否输出) ( 行 …

c语言练习12周(6~10)

以下程序调用递归函数fun实现求n!&#xff0c;请补充代码。 题干以下程序调用递归函数fun实现求n!&#xff0c;请补充代码。 int fun(int n) { int c; /****************/ /****************/ else cn*fun(n-1); …

【云备份】文件操作实用工具类设计

文章目录 为什么要单独设计文件工具类&#xff1f;整体实现Filesize ——文件大小stat接口 LastMTime ——最后一次修改时间LastATime —— 最后一次访问时间FileName —— 文件名称GetPostLen ——获取文件指定位置 指定长度的数据GetContnet —— 读取文件数据SetContent ——…

在 Linux 中重命名文件和目录

目录 前言 使用 mv 命令重命名文件和目录 通过组合 mv、find 和 exec 命令重命名与某个模式匹配的多个文件 使用 rename 命令轻松重命名多个文件 总结 前言 在这篇基本命令行教程中&#xff0c;你将学习在 Linux 终端重命名文件和目录的各种方法。 如何在 Linux 终端中重命…

Sublime Text 3 安装离线插件 anaconda

1 下载 Sublime Text 3 免安装版 Download - Sublime Text 2 下载 Package Control&#xff0c;放到 Sublime Text Build 3211\Data\Installed Packages 目录下。 Installation - Package Control 3 页面搜索 anaconda anaconda - Search - Package Control Anaconda - Pac…

彩纸屋在线少儿编程源码/scratch在线编程系统/培训管理系统源码/在线培训系统源码PHP

源码简介&#xff1a; 彩纸屋在线少儿编程源码&#xff0c;它是scratch在线编程系统&#xff0c;作为培训管理系统源码/在线培训系统源码&#xff0c;采用PHP源码。 彩纸屋是全国首家提供scratch开源定制和少儿编程培训管理系统源代码的服务商&#xff0c;彩纸屋提供的scratc…

WebGL/threeJS面试题扫描与总结

什么是 WebGL&#xff1f;什么是 Three.js&#xff1f;请解释three.js中的WebGL和Canvas的区别&#xff1f; WebGL(全写Web Graphics Library)是一种3D绘图协议&#xff0c;这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起&#xff0c;通过增加OpenGL ES 2.0的一个…
最新文章