综合案例 计算器(模板+命令+责任链)

计算器

一个简单的计算器可以分为3部分。1是校验部分,校验表达式的合法性,这一部分可以通过这责任链来依次校验;2是表达式的解析部分,将表达式中的数据和符号拆分出来;3是运算部分,这一部分直接使用之前写的命令模式的相关代码。

责任链

通过责任链将所有的步骤串起来,切步骤中的节点可以根据自己的想法进行替换,排序,十分灵活。

  • 责任链抽象层:用一个模板方法定义好链条的调用流程。向子类暴露一个hand方法实现具体的处理逻辑。
package behavioralpattern.patterncase.caseone.handler;
/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:26
 * @description: 责任链抽象层
 *
 * 表达式合法校验,可以使用责任链模式 合法返回ture,
 * 不合法返回false和失败的原因 在抽象层使用模板,
 * 规定好流程,具体的流程细节由子类实现。
 */
public abstract class AbsHandler {
    private AbsHandler handler;

    public void setNext(AbsHandler handler) {
        this.handler = handler;
    }

    // 具体的处理逻辑,交给子类来实现
    protected abstract Resp hand(Resp resp);

    // 启动方法
    public final void start(Resp resp) {
        // --清空缓存--
        resp.cleanCache();
        // 先执行当前的处理逻辑
        resp = hand(resp);
        // 判断处理结果,根据结果判断是否执行下一步
//        if (!resp.ok()) {
//            // 中断处理
//            return;
//        }
        // 继续执行
        if(handler!=null){
            handler.start(resp);
        }
    }
}
  • 处理的对象:定义好处理的状态,返回信息,处理的参数、处理的结果、以及中间过程可能会用到的对象、方面在链路上使用。
package behavioralpattern.patterncase.caseone.handler;

import java.util.Stack;
/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:27
 * @description: 处理对象
 */
public class Resp {
    //是否校验通过
    private boolean ok = true;
    // 失败的消息
    private final StringBuffer msg = new StringBuffer();
    // 校验的数据
    private String data;
    // 中间数据缓存 数字
    public  Stack<String> nums = new Stack<>();
    // 中间数据缓存 符号
    public  Stack<Character> opts = new Stack<>();

    public Resp(String data) {
        this.data = data;
    }

    // 清空缓存
    public void cleanCache() {
        nums.clear();
        opts.clear();
    }

    public boolean ok() {
        return ok;
    }

    public Resp setOk(boolean ok) {
        this.ok = ok;
        return this;
    }

    public String msg() {
        return msg.toString();
    }

    public Resp setMsg(String msg) {
        this.msg.append("\n");
        this.msg.append("err:");
        this.msg.append(msg);
        return this;
    }

    public String data() {
        return data;
    }

    public Resp setData(String data) {
        this.data = data;
        return this;
    }
	// 方便结果显示
    @Override
    public String toString() {
        return "Resp{" +
                "ok= " + ok +
                ", msg= \" " + msg + " \"" +
                ", data= \" " + data +
                " \" }";
    }
}

校验部分

  • 校验输入的字符串是不是空串,切去除字符串中的空格,便于之后进行解析。
package behavioralpattern.patterncase.caseone.handler;

import behavioralpattern.patterncase.caseone.Utils;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:31
 * @description: 空串校验,并去除字符串中的空格
 */
public class CheckEmpty extends AbsHandler {
    @Override
    protected  Resp hand(Resp resp) {
        String s = Utils.get(resp);
        // 去除所有空格
        String replace = s.replace(" +", "");
        // 判断是不是空串
        if (replace == "") {
            resp.setOk(false).setMsg("字符串为空字符串");
        }
        return Utils.set(resp, replace);
    }
}
  • 校验字符的合法性( 应该只包含0123456789 . + - * / ( ) )
package behavioralpattern.patterncase.caseone.handler;
import behavioralpattern.patterncase.caseone.Utils;
/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:36
 * @description: 校验字符的合法性,是否只包含 "0123456789 . + - * \/ ( )"
 */

public class CheckChars extends AbsHandler{

    @Override
    protected  Resp hand(Resp resp) {
        String s = Utils.get(resp);
        String legal = "0123456789.+-*/()";
        for (char c : s.toCharArray()) {
            if (!legal.contains(String.valueOf(c))) {
                resp.setMsg("字符不合法"+c).setOk(false);
            }
        }
        return resp;
    }
}
  • 加减乘除符号的使用是否合法
package behavioralpattern.patterncase.caseone.handler;

import behavioralpattern.patterncase.caseone.Utils;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:35
 * @description: 符号合法性校验
 */
public class CheckOpt extends AbsHandler {
    @Override
    protected  Resp hand(Resp resp) {
        // 符号前后得有数字或是()
        String s = Utils.get(resp);
        for (int i = 0; i < s.length(); i++) {
            char one = s.charAt(i);
            if (Utils.checkOpt(one)) {
                if (i > 0 && i < s.length() - 1) {
                    char before = s.charAt(i - 1);
                    char after = s.charAt(i + 1);
                    if(!Utils.checkNum(before) && before!=')'){
                        if(before!='('){
                            resp.setMsg("操作符之前不能 不是数字或者')':"+(i+1)).setOk(false);
                        }
                    }
                    if(!Utils.checkNum(after) && after!='('){
                        resp.setMsg("操作符之后不能 不是数字或者'(':"+(i+1)).setOk(false);
                    }
                }
                if(i==0){
                    if(one=='*' || one=='/'){
                        resp.setMsg("/ 和 * 不能在开头:"+(i+1)).setOk(false);
                    }
                }
                if(i==s.length()-1){
                    if(Utils.checkOpt(one)){
                        // 不能以符号结尾
                        resp.setMsg("不能以符号结尾:"+(i+1)).setOk(false);
                    }
                }
            }
        }
        return resp;
    }
}

  • 小数点的使用是否合法校验:小数点前后都得是数字
package behavioralpattern.patterncase.caseone.handler;
import behavioralpattern.patterncase.caseone.Utils;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:40
 * @description: 小数点校验
 */
public class CheckPoint extends AbsHandler{

    @Override
    protected  Resp hand(Resp resp) {
        String s = Utils.get(resp);
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(c=='.'){
                // 点的要求,前后都得是数字
                if(i==0||i==s.length()-1){
                    resp.setOk(false).setMsg("小数点使用不符合规范:"+(i+1));
                }
                if(i>0&&i<s.length()-1){
                    char before = s.charAt(i - 1);
                    char after = s.charAt(i+1);
                    if(!Utils.checkNum(before)&&!Utils.checkNum(after)){
                        resp.setOk(false).setMsg("小数点使用不符合规范:"+(i+1));
                    }
                }
            }
        }
        return resp;
    }
}
  • 括号的合法性校验
package behavioralpattern.patterncase.caseone.handler;

import behavioralpattern.commandpattern.command.*;
import behavioralpattern.patterncase.caseone.Utils;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 10:00
 * @description:
 * 计算节点,最后一个节点
 * 结合命令模式进行计算
 */
public class CalculateHandler extends AbsHandler{
    private static final Map<Character, Command> commands = new HashMap<>();
    static {
        commands.put('+',new AddC());
        commands.put('-',new SubC());
        commands.put('*',new MulC());
        commands.put('/',new DivC());
    }
    private void exec(Resp resp){
        // 获取运算命令
        Character opt = resp.opts.pop();
        Command command = commands.get(opt);
        // 获取2个数值
        String towN = resp.nums.pop();
        String oneN = resp.nums.pop();
        // 进行运算,将结果重新放入数值列表中。
        BigDecimal execute = command.execute(new BigDecimal(oneN), new BigDecimal(towN));
        resp.nums.push(execute.toString());
    }
    @Override
    protected  Resp hand(Resp resp) {
        // 前面的处理校验都通过了才进行计算
        if(resp.ok()){
            String s = Utils.get(resp);
            int len = s.toCharArray().length;
            // 循环获取字符,通过i和cur截取出数值
            int i = 0,cur = 0;
            for (; i < len; i++) {
                char one = s.charAt(i);
                if(Utils.checkOpt(one)||Utils.checkBracket(one)){
                    // 获取数值
                    if(i!=cur){
                        resp.nums.push(s.substring(cur,i));
                    }
                    // + - 单独处理,可能是正负号
                    if(one=='-'||one=='+'){
                        if(i==0||(s.charAt(i-1)=='(')){
                            continue;
                        }
                    }
                    cur = i+1;

                    // 遇到 ) 进行计算,遇到优先级高的,先计算
                    // 先判断是不是 )
                    if(one==')'){
                        if (resp.opts.peek()!='(') {
                            exec(resp);
                        }
                        // 弹出 (
                        resp.opts.pop();
                    } else if (Utils.checkOpt(one)) {
                        if (!resp.opts.isEmpty()&&
                                (resp.opts.peek()=='*'||resp.opts.peek()=='/')) {
                            exec(resp);
                        }
                        resp.opts.push(one);
                    }else {
                        resp.opts.push(one);
                    }
                }
            }
            // 获取数值
            if(i!=cur){
                resp.nums.push(s.substring(cur,i));
            }
            while (!resp.opts.isEmpty()){
                exec(resp);
            }
            resp.setData(resp.nums.peek());
        }
        return resp;
    }
}

计算节点

  • 进行表达式的解析,解析之后进行计算
package behavioralpattern.patterncase.caseone.handler;

import behavioralpattern.commandpattern.command.*;
import behavioralpattern.patterncase.caseone.Utils;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 10:00
 * @description:
 * 计算节点,最后一个节点
 * 结合命令模式进行计算
 */
public class CalculateHandler extends AbsHandler{
    private static final Map<Character, Command> commands = new HashMap<>();
    static {
        commands.put('+',new AddC());
        commands.put('-',new SubC());
        commands.put('*',new MulC());
        commands.put('/',new DivC());
    }
    private void exec(Resp resp){
        // 获取运算命令
        Character opt = resp.opts.pop();
        Command command = commands.get(opt);
        // 获取2个数值
        String towN = resp.nums.pop();
        String oneN = resp.nums.pop();
        // 进行运算,将结果重新放入数值列表中。
        BigDecimal execute = command.execute(new BigDecimal(oneN), new BigDecimal(towN));
        resp.nums.push(execute.toString());
    }
    @Override
    protected  Resp hand(Resp resp) {
        // 前面的处理校验都通过了才进行计算
        if(resp.ok()){
            String s = Utils.get(resp);
            int len = s.toCharArray().length;
            // 循环获取字符,通过i和cur截取出数值
            for (int i = 0,cur = 0; i < len; i++) {
                char one = s.charAt(i);
                if(Utils.checkOpt(one)||Utils.checkBracket(one)){
                    // 获取数值
                    if(i!=cur){
                        resp.nums.push(s.substring(cur,i));
                    }
                    cur = i+1;
                    // 遇到 ) 进行计算,遇到优先级高的,先计算
                    // 先判断是不是 )
                    if(one==')'){
                        if (resp.opts.peek()!='(') {
                            exec(resp);
                        }
                        // 弹出 (
                        resp.opts.pop();
                    } else if (Utils.checkOpt(one)) {
                        if (!resp.opts.isEmpty()&&
                                (resp.opts.peek()=='*'||resp.opts.peek()=='/')) {
                            exec(resp);
                        }
                        resp.opts.push(one);
                    }else {
                        resp.opts.push(one);
                    }
                }
            }
            while (!resp.opts.isEmpty()){
                exec(resp);
            }
            resp.setData(resp.nums.peek());
        }
        return resp;
    }
}

模板

定义一个模板,留一个接口供子类选择和组合使用那些节点,定义模板方法获取运行结果。

  • 抽象层
package behavioralpattern.patterncase.caseone.handlerconf;

import behavioralpattern.commandpattern.command.*;
import behavioralpattern.patterncase.caseone.handler.AbsHandler;
import behavioralpattern.patterncase.caseone.handler.Resp;

import java.util.HashMap;
import java.util.Map;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:45
 * @description:
 * 计算器模板的抽象层
 */
public abstract class AbsCalculatorTemplate {
    // 聚合处理校验责任链
    private AbsHandler absHandler;

    public AbsCalculatorTemplate() {
        this.absHandler = setHandler();
    }

    // 留个接口给子类配置
    protected abstract AbsHandler setHandler();
    // 进行运算获取结果
    public final String execute(Resp r){
        absHandler.start(r);
        System.out.println(r);
        if (r.ok()){
            return r.data();
        }
        return r.msg();
    }
}
  • 子类
package behavioralpattern.patterncase.caseone.handlerconf;

import behavioralpattern.patterncase.caseone.handler.*;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 10:30
 * @description:
 * 计算器本体
 */
public class Calculator extends AbsCalculatorTemplate{
    @Override
    protected AbsHandler setHandler() {
        // 构造责任链,返回给父类使用
        // 校验是否为空串,并去除其中的空格
        AbsHandler checkEmpty = new CheckEmpty();
        // 校验字符是否合法
        AbsHandler checkChars = new CheckChars();
        // 校验符号使用是否合法
        AbsHandler checkOpt = new CheckOpt();
        // 校验小数点使用是否合法
        AbsHandler checkPoint = new CheckPoint();
        // 校验括号使用是否合法
        AbsHandler checkBracket = new CheckBracket();
        // 计算节点
        AbsHandler calculateHandler = new CalculateHandler();
        // 串起来
        checkEmpty.setNext(checkChars);
        checkChars.setNext(checkOpt);
        checkOpt.setNext(checkPoint);
        checkPoint.setNext(checkBracket);
        checkBracket.setNext(calculateHandler);

        return checkEmpty;
    }
}

客户端

package behavioralpattern.patterncase.caseone;

import behavioralpattern.patterncase.caseone.handler.*;
import behavioralpattern.patterncase.caseone.handlerconf.AbsCalculatorTemplate;
import behavioralpattern.patterncase.caseone.handlerconf.Calculator;

/**
 * @author tx
 * @version 1.0
 * @date 2024/1/16 9:16
 * @description:
 * 综合示例 命令模式+模板模式+责任链模式
 */
public class CaseOne {
    public static void main(String[] args) {
        // 构造参数对象
        // 表达式
        String expression = "(1+1.5)*(2*(5-1)/2)*(1)";
        //括号不匹配
        Resp stringResp = new Resp(expression);
        // 创建一个计算器
        AbsCalculatorTemplate calculator = new Calculator();
        String execute = calculator.execute(stringResp);
    }
}

详细代码见gitee

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值