这个作业属于哪个课程 | https://bbs.csdn.net/forums/ssynkqtd-05 |
这个作业要求在哪里 | https://bbs.csdn.net/topics/617294583 |
这个作业的目标 | 完成一个具有可视化界面的计算器 |
Gitcode项目地址
1. 项目功能展示
2. PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 50 | 30 |
•Estimate | • 估计这个任务需要多少时间 | 15 | 10 |
Development | 开发 | 2020 | 2190 |
• Analysis | • 需求分析 (包括学习新技术) | 800 | 700 |
• Design Spec | • 生成设计文档 | 60 | 65 |
• Design Review | • 设计复审 | 60 | 65 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 40 | 30 |
• Design | • 具体设计 | 60 | 90 |
• Coding | • 具体编码 | 1000 | 1200 |
• Code Review | • 代码复审 | 45 | 60 |
Reporting | 报告 | 85 | 70 |
• Test Repor | • 测试报 | 20 | 25 |
• Size Measurement | • 计算工作量 | 20 | 15 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 45 | 30 |
合计 | 2140 | 2290 |
3. 解题思路描述
要求实现一个具有图形化界面简易计算器
-
编程语言选择。本程序采用C#语言下的 WPF框架实现图形化界面。
-
界面设计。为使界面美观,我使用了HandyControl开源控件库,并使用XAML对界面进行合理布局,程序拥有“标准”和“科学”两种模式,可以通过模式切换按钮进行切换。
-
核心功能。
本程序实现了如下作业要求中的基本功能和附加功能
功能:具有基本功能的计算器 实现加、减、乘、除、归零基本操作。
附加功能:具有科学计算的计算器 实现次方、幂、三角函数等操作。
具体实现方法如下:
(1)通过Button控件获得用户输入数据
(2)将输入数据整理为标准的中缀表达式
(3)转换中缀表达式为后缀表达式
(4)计算后缀表达式
(5)通过TextBlock控件输出结果
4. 接口设计和实现过程
1. 根据数据处理方式,为按钮划分类别,相同类别按钮在同一个点击事件函数中进行处理
2. 通过不同事件函数处理输入数据,并将处理后的数据存储到ArrayList
3. 设计Analysis()函数对ArrayList中的表达式进行分析,并返回结果
5.关键代码展示
- 返回运算优先级
private int OperatorLevel(string op) { int level = 0; switch (op) { case "+": level = 0; break; case "-": level = 0; break; case "*": level = 1; break; case "/": level = 1; break; case "%": level = 1; break; case "1/x": level = 1; break; case "^": level = 2; break; case "!": level = 3; break; case "√": level = 3; break; case "log(": case "ln(": case "sin(": case "cos(": case "tan(": case "asin(": case "acos(": case "atan(": level = 4; break; } return level; }
- 单次运算
private bool Process() { if (operators.Count <= 0) { return false; } string op = operators.Pop(); if (op == "log(" || op == "√" || op == "ln(" || op == "sin(" || op == "cos(" || op == "tan(" || op == "asin(" || op == "acos(" || op == "atan(" || op == "!" || op == "1/x") { if (numbers.Count <= 0) { return false; } string num = numbers.Pop(); string result = SingleOperator(num, op); if (result == "") { return false; } else numbers.Push(result); } else { if (numbers.Count < 2) { return false; } string num2 = numbers.Pop(); string num1 = numbers.Pop(); string result = DoubleOperator(num1, num2, op); if (result == "") { return false; } else numbers.Push(result); } return true; }
-
表达式分析
public string Analysis(ArrayList arrayList) { int i = 0; while (i < arrayList.Count) { string value = (string)arrayList[i]; if (isNumberic(value)) { if (value == "e") value = Math.Exp(1).ToString(); else if (value == "π") value = Math.PI.ToString(); numbers.Push(value); } else if (IsOperator(value)) { if (operators.Count == 0 || (operators.Peek().Contains("(") && value != ")") || value.Contains("(")) operators.Push(value); else if (OperatorLevel(value) > OperatorLevel(operators.Peek())) operators.Push(value); else if (value == ")") { while (!operators.Peek().Contains("(")) { if (operators.Count <= 0) { isError = true; return ""; } string op = operators.Pop(); if (op == "!" || op == "1/x" || op == "√") { if (numbers.Count <= 0) { isError = true; return ""; } string num = numbers.Pop(); string result = SingleOperator(num, op); if (result == "") { isError = true; return ""; } else numbers.Push(result); } else if (op == "log(" || op == "ln(" || op == "sin(" || op == "cos(" || op == "tan(" || op == "asin(" || op == "acos(" || op == "atan(") { isError = true; return ""; } else { if (numbers.Count < 2) { isError = true; return ""; } string num2 = numbers.Pop(); string num1 = numbers.Pop(); string result = DoubleOperator(num1, num2, op); if (result == "") { isError = true; return ""; } else numbers.Push(result); } } if (operators.Peek() == "(") operators.Pop(); else if (operators.Peek().Contains("(")) { if (operators.Count <= 0 || numbers.Count <= 0) { isError = true; return ""; } string op = operators.Pop(); string num = numbers.Pop(); string result = SingleOperator(num, op); if (result == "") { isError = true; return ""; } else numbers.Push(result); } else { isError = true; return ""; } } else { if (Process()) continue; else { isError = true; return ""; } } } i++; } while (operators.Count != 0 && numbers.Count >= 1) { if (!Process()) { isError = true; return ""; } } if (operators.Count == 0 && numbers.Count == 1) return numbers.Pop(); return ""; }
6. 程序性能改进
为了改进程序性能,我考虑了以下几个方法:
-
数据绑定优化:使用轻量级的数据绑定,避免过多的绑定和频繁的属性更改通知。可以使用一次性绑定或延迟加载等技术来减少不必要的数据绑定。
-
控件优化:使用合适的控件,避免使用过多的复杂控件或多层嵌套的控件。精简界面,去除不必要的控件和视觉效果。
-
数据缓存:如果某些数据在计算中频繁使用,可以考虑将其缓存,避免重复计算,提高计算速度。
-
使用合适的数据结构和算法:根据具体的计算需求,选择合适的数据结构和算法。
-
内存管理优化:注意及时释放不再使用的资源,避免内存泄漏。
7. 单元测试展示
- 单元测试代码:
using Microsoft.VisualStudio.TestTools.UnitTesting; using Logic; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections; namespace Logic.Tests { [TestClass()] public class LogicalTests { [TestMethod()] public void AnalysisTest() { Logical logic = new Logical(); ArrayList arrayList = new ArrayList(new string[] { "1", "+", "2", "-", "3" }); string result = logic.Analysis(arrayList); Assert.AreEqual("0", result); arrayList = new ArrayList(new string[] { "(", "π", "+", "π", ")", "*", "2" }); result = logic.Analysis(arrayList); Assert.AreEqual("12.566370614359172", result); arrayList = new ArrayList(new string[] { "(", "√", "4", "+", "3", "%", "2", ")", "*", "3" }); result = logic.Analysis(arrayList); Assert.AreEqual("9", result); arrayList = new ArrayList(new string[] { "-3", "!" }); result = logic.Analysis(arrayList); Assert.AreEqual("-6", result); arrayList = new ArrayList(new string[] { "2", "1/x" }); result = logic.Analysis(arrayList); Assert.AreEqual("0.5", result); arrayList = new ArrayList(new string[] { "asin(", "1", ")", "+", "acos(", "1", ")", "+", "atan(", "1", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("135", result); arrayList = new ArrayList(new string[] { "2", "^", "(", "3", ")", "*", "4" }); result = logic.Analysis(arrayList); Assert.AreEqual("32", result); arrayList = new ArrayList(new string[] { "(", "1", "+", "2", ")", "!" }); result = logic.Analysis(arrayList); Assert.AreEqual("6", result); arrayList = new ArrayList(new string[] { "sin(", "0", ")", "+", "cos(", "0", ")", "+", "tan(", "45", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("2.0", result); arrayList = new ArrayList(new string[] { "log(", "10", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("1", result); arrayList = new ArrayList(new string[] { "log(", "1.1", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("0.04139268515822508", result); arrayList = new ArrayList(new string[] { "0", "+", "1", "+", "2", "+", "3", "+", "4", "+", "5", "+", "6", "+", "7", "+", "8", "+", "9" }); result = logic.Analysis(arrayList); Assert.AreEqual("45", result); arrayList = new ArrayList(new string[] { "1.2", "+", "3.4" }); result = logic.Analysis(arrayList); Assert.AreEqual("4.6", result); arrayList = new ArrayList(new string[] { "e", "^", "(", "2", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("7.3890560989306495", result); arrayList = new ArrayList(new string[] { "(", "1", "+", "2", ")", "^", "(", "3", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("27", result); arrayList = new ArrayList(new string[] { "3", "*", "4", "/", "2" }); result = logic.Analysis(arrayList); Assert.AreEqual("6", result); arrayList = new ArrayList(new string[] { "asin(", "1", ")", "+", "acos(", "1", ")", "+", "atan(", "1", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("135", result); arrayList = new ArrayList(new string[] { "ln(", "e", ")" }); result = logic.Analysis(arrayList); Assert.AreEqual("1", result); arrayList = new ArrayList(new string[] { "3", "!" }); result = logic.Analysis(arrayList); Assert.AreEqual("6", result); arrayList = new ArrayList(new string[] { "1", "/", "0" }); logic.Analysis(arrayList); Assert.AreEqual(1, logic.code); arrayList = new ArrayList(new string[] { "0", "1/x" }); logic.Analysis(arrayList); Assert.AreEqual(1, logic.code); arrayList = new ArrayList(new string[] { "1", "%", "0" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "asin(", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "acos(", "-2", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "atan(", "-2", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "√", "-2" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "log(", "0", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "ln(", "0", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "1.1", "!" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "tan(", "90", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "asin(", "2", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "acos(", "2", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "atan(", "2", ")" }); logic.Analysis(arrayList); Assert.AreEqual(2, logic.code); arrayList = new ArrayList(new string[] { "1", "/" }); logic.Analysis(arrayList); Assert.IsTrue(logic.isError); arrayList = new ArrayList(new string[] { "a" }); logic.Analysis(arrayList); Assert.IsTrue(logic.isError); arrayList = new ArrayList(new string[] { "a", "+" }); logic.Analysis(arrayList); Assert.IsTrue(logic.isError); } } }
- 测试结果:
- 测试覆盖率:
8. 异常处理展示
9. 心得体会
作为第一次使用WPF开发的初学者,编写一个科学计算器程序是一项具有挑战性和趣味性的任务。通过这个项目,我积累了很多关于WPF开发和科学计算的知识。以下是我的心得体会:
首先,良好的代码组织非常重要。应尽量将不同的功能分离为独立的类和方法,以确保代码结构清晰明了。可以根据功能进行模块化设计,每个模块负责不同的计算操作或UI交互。
其次,合理利用WPF的布局和控件功能来构建用户界面。WPF提供了丰富的布局控件,如Grid、StackPanel等,以及各种常用的控件,如Button、TextBox等,可以灵活地实现界面布局和用户输入输出。
另外,WPF的样式和模板功能能够帮助我们实现界面的美化和定制化。可以为不同的控件定义特定的样式,以确保用户界面的一致性和美观性。
还需要注意用户输入的验证和错误处理。对于科学计算器而言,用户输入的数据可能存在格式错误和计算溢出等问题,因此我们需要相应的验证机制并向用户提供明确的错误提示。
最后,测试是不可忽视的一步。要确保程序的稳定性和正确性,可以编写单元测试用例,覆盖各种计算操作,并对边界情况进行测试。
综上所述,只有通过良好的代码组织、合理利用WPF的布局和控件、样式和模板功能,以及对用户输入的验证和错误处理,我们才能开发出一个功能完善、用户友好的科学计算器程序。通过这个项目,我不仅深入了解和应用了WPF技术,还提升了编程能力和问题解决能力。这个项目对我的职业发展和学习路径都产生了积极的影响。