Java-Jython实现可视化计算器

作业基本信息…

这个作业属于哪个课程FZU-CS-SE
这个作业要求在哪里https://bbs.csdn.net/forums/ssynkqtd-05?spm=1001.2014.3001.6685
这个作业的目标完成一个具有可视化界面的计算器
其他参考文献

IDE: IDEA 2022.5

OS: macOS Ventura 13.3.1

JDK: 1.8

项目依赖管理:Maven

mac上前端界面(windows上似乎没有这么美观…)
在这里插入图片描述

FZU-CS-SE-Calculator

运行方式:

  1. 直接exe启动,需要jre1.8以上的java环境配置
  2. 在项目目录下运行如下命令(需要有maven环境配置和jdk1.8以上配置)
mvn clean test
java -jar Calculator-1.0-SNAPSHOT.jar

Gitcode项目地址

我没有GitCode账号,就使用了我的github账号仓库地址

BlackBear2003/FZU-CS-SE-Calculator (github.com)

PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2020
• Estimate• 估计这个任务需要多少时间240300
Development开发200200
• Analysis• 需求分析 (包括学习新技术)00
• Design Spec• 生成设计文档2020
• Design Review• 设计复审2020
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)55
• Design• 具体设计3030
• Coding• 具体编码8080
• Code Review• 代码复审2525
• Test• 测试(自我测试,修改代码,提交修改)2020
Reporting报告2020
• Test Repor• 测试报告55
• Size Measurement• 计算工作量55
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划1010
合计280350

解题思路描述

因为个人比较熟悉Java语言,所以打算使用Java语言尝试一下开发这样的软件,然后了解到Java可以方便的调用其他脚本语言的引擎API,所以一开始的思路是:

使用Java AWT包作为前端UI界面面向用户使用,计算部分代码是将用户输入生成的字符串作为一个表达式,利用java对这个表达式做正则运算,让这个表达式符合javaScript的语法规范,使用JavaScript脚本引擎运行eval函数,利用脚本语言的特性方便实现我的功能。

但是后面发现我使用的Jdk1.8并不支持ES6以上的特性,所以没办法支持" ** "这个方便计算幂运算,所以我更换了我的脚本引擎,使用Jython这个开源代码库,将表达式放到python语句中执行。然后成功实现了题目中提到的幂运算和三角函数运算,并且也实现了阶乘运算。并且支持用户自由切换三角函数是要以角度制(dgr)还是弧度制(rad)来计算。

最后引入了JTest框架对本项目书写了单元测试和覆盖率评估。

问题1

  • Jdk1.8自带的javax.script包中的JS引擎并不支持ES6以上的特性

Solution:

更换为Python脚本执行。

问题2

  • 用户输入的表达式并不符合脚本语言的规范

Solution:

使用Java正则表达式,将’sin(‘替换为’math.sin(’, 将 x!替换为 math.factorial(x) , 将x ^ y 替换为 x ** y

问题3

  • 希望实现三角函数是要以角度制(dgr)还是弧度制(rad)来计算的切换。

Solution:

维护一个变量isRad,通过这个变量来判断当前的计算方式是什么,然后通过事件监听机制,获取到产生事件的按钮变量的引用,修改它在堆中的text属性,让前端页面刷新。同时计算部分只需要一个分支判断就可以判断是使用哪一种计算方式。

接口设计和实现过程

/*
 * 把前端Frame初始化好,同时让脚本执行器引入必要的python库(比如math库)
 */
Calculator.initFrame();

/*
 * 利用了Java AWT的消息机制特性,在用户点击按钮时接受对应事件,在这个函数中实现针对不同事件的不同逻辑处理
 */
ActionListener.actionPerformed(ActionEvent e);

关键代码展示

public class Calculator {

 private final PythonInterpreter interpreter;
 private JFrame frame;
 private JTextField textField;
 private String lastResult = "";
 private boolean isRad = false;
 private JButton evalBtn;
 private JButton clearBtn;
 private JButton switchRadDgrBtn;

 public Calculator() {

   initFrame();

   System.setProperty("python.import.site", "false");
   interpreter = new PythonInterpreter();
   interpreter.exec("import math");
 }

 void initFrame() {
   this.frame = new JFrame("计算器");
  	...
 }

 String toEvaluableFactorialExpression(String expression) {
   String pattern = "(\\d+)!";
   Pattern regex = Pattern.compile(pattern);
   Matcher matcher = regex.matcher(expression);
   StringBuffer output = new StringBuffer();
   while (matcher.find()) {
     int num = Integer.parseInt(matcher.group(1));
     String replacement = "math.factorial(" + num + ")";
     matcher.appendReplacement(output, replacement);
   }
   matcher.appendTail(output);
   return output.toString();
 }
 public class ButtonClickListener implements ActionListener {

   public void actionPerformed(ActionEvent e) {
     String command = e.getActionCommand();
     Object source = e.getSource();

     if (command.equals("=")) {
       String expression = textField.getText();
       // 在用户输入的表达式中添加 "Math."
       expression = expression.replaceAll("\\^", " ** ");
       if (isRad) {
         expression = expression.replaceAll("(sin|cos|tan)\\(", "math.$1(");
       } else {
         expression = expression.replaceAll("(sin|cos|tan)\\(", "math.$1( math.pi / 180.0 *");
       }
       expression = toEvaluableFactorialExpression(expression);

       System.out.println(expression);
       Object result = null;
       try {
         result = interpreter.eval(expression);
       } catch (PyException ex) {
         result = ex.getMessage().split(":")[0];
       }
       lastResult = result.toString();
       if (lastResult.endsWith("L")) {
         lastResult = lastResult.substring(0, lastResult.length()-1);
       }

       textField.setText(lastResult);
     } else if (command.equals("AC")) {
       textField.setText("");
       lastResult = "";
     } else if (command.equals("rad") || command.equals("dgr")) {
       isRad = !isRad;
       JButton clickedButton = (JButton) source;
       clickedButton.setText(isRad ? "rad" : "dgr");
     } else {
       if (!lastResult.isEmpty()) {
         textField.setText(lastResult + command);
         lastResult = "";
       } else {
         textField.setText(textField.getText() + command);
       }
     }
   }
 }
}

性能改进

也许可以通过自己手写表达式判断,利用数据结构,将中缀表达式转为后缀表达式,利用Java的Math库,完成原生计算,不必依赖脚本语言的引擎,可以大大减小打包文件的大小。

单元测试

使用JTest框架对本项目书写了单元测试和覆盖率评估。

例如:

  @Test
  public void testAddition() {
    calculator.getTextField().setText("2+3");
    calculator.getEvalBtn().doClick();
    assertEquals("5", calculator.getLastResult());
  }

对每一种计算都书写了类似逻辑的单元测试:
在这里插入图片描述

覆盖率报告如下:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值