软件工程实践第一次作业

发布时间 2023-09-29 23:58:37作者: Alpha-code

Calculator

作业基本信息

这个作业属于哪个课程 https://bbs.csdn.net/forums/ssynkqtd-05
这个作业要求在哪里 https://bbs.csdn.net/topics/617294583
这个作业的目标 完成一个具有可视化界面的计算器

Gitcode项目地址

 

1. 项目功能展示

 

2. PSP表格

PSPPersonal 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. 解题思路描述

    要求实现一个具有图形化界面简易计算器

  1. 编程语言选择。本程序采用C#语言下的 WPF框架实现图形化界面。

  2. 界面设计。为使界面美观,我使用了HandyControl开源控件库,并使用XAML对界面进行合理布局,程序拥有“标准”和“科学”两种模式,可以通过模式切换按钮进行切换。

  3. 核心功能。

    本程序实现了如下作业要求中的基本功能和附加功能

      功能:具有基本功能的计算器 实现加、减、乘、除、归零基本操作。

           附加功能:具有科学计算的计算器 实现次方、幂、三角函数等操作。

    具体实现方法如下:

      (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;
            }
    View Code
  • 单次运算
            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;
            }
    View Code
  • 表达式分析

            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 "";
            }
    View Code

6. 程序性能改进

为了改进程序性能,我考虑了以下几个方法:

  1. 数据绑定优化:使用轻量级的数据绑定,避免过多的绑定和频繁的属性更改通知。可以使用一次性绑定或延迟加载等技术来减少不必要的数据绑定。

  2. 控件优化:使用合适的控件,避免使用过多的复杂控件或多层嵌套的控件。精简界面,去除不必要的控件和视觉效果。

  3. 数据缓存:如果某些数据在计算中频繁使用,可以考虑将其缓存,避免重复计算,提高计算速度。

  4. 使用合适的数据结构和算法:根据具体的计算需求,选择合适的数据结构和算法。

  5. 内存管理优化:注意及时释放不再使用的资源,避免内存泄漏。

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);
            }
        }
    }
    View Code
  • 测试结果:

  • 测试覆盖率:

 

8. 异常处理展示

 

9. 心得体会 

  作为第一次使用WPF开发的初学者,编写一个科学计算器程序是一项具有挑战性和趣味性的任务。通过这个项目,我积累了很多关于WPF开发和科学计算的知识。以下是我的心得体会:

  首先,良好的代码组织非常重要。应尽量将不同的功能分离为独立的类和方法,以确保代码结构清晰明了。可以根据功能进行模块化设计,每个模块负责不同的计算操作或UI交互。

  其次,合理利用WPF的布局和控件功能来构建用户界面。WPF提供了丰富的布局控件,如Grid、StackPanel等,以及各种常用的控件,如Button、TextBox等,可以灵活地实现界面布局和用户输入输出。

  另外,WPF的样式和模板功能能够帮助我们实现界面的美化和定制化。可以为不同的控件定义特定的样式,以确保用户界面的一致性和美观性。

  还需要注意用户输入的验证和错误处理。对于科学计算器而言,用户输入的数据可能存在格式错误和计算溢出等问题,因此我们需要相应的验证机制并向用户提供明确的错误提示。

  最后,测试是不可忽视的一步。要确保程序的稳定性和正确性,可以编写单元测试用例,覆盖各种计算操作,并对边界情况进行测试。

  综上所述,只有通过良好的代码组织、合理利用WPF的布局和控件、样式和模板功能,以及对用户输入的验证和错误处理,我们才能开发出一个功能完善、用户友好的科学计算器程序。通过这个项目,我不仅深入了解和应用了WPF技术,还提升了编程能力和问题解决能力。这个项目对我的职业发展和学习路径都产生了积极的影响。