# 软件工程实验 **Repository Path**: Kky233/software-experiment ## Basic Information - **Project Name**: 软件工程实验 - **Description**: 软件工程课程实验 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 0 - **Created**: 2023-09-11 - **Last Updated**: 2024-12-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 软件工程实验 ### 介绍 软件工程课程实验 ### **软件工程实验一:个人编程技能和git操作** #### 1. git 基本操作 要求每个学生开始管理自己的源代码: 每个同学申请一个 [GitHub](https://github.com/) 的账号,或者 [码云](https://gitee.com/) 账号,存放源程序和其他文档。 安装、配置git。基础教程参考[git 5分钟教程](https://www.runoob.com/w3cnote/git-five-minutes-tutorial.html) 基本操作示例 ``` git clone 代码仓库 //克隆代码仓库到本地。代码仓库可以是一个 URL 或者本地路径。 git add . //将所有修改或新增的文件添加到暂存区。. 表示当前目录下的所有文件和文件夹。 git commit -m "提交说明" //提交暂存区中的修改并添加一条提交说明。 git push origin master //将本地的提交推送到远程仓库的 master 分支。 ``` #### 2. 编程基本功练习 ##### (1)编程实现Hello world。 全部用命令行工具和notepad编辑器,不用Eclipse 等集成编辑环境,每人手工创建并编译一个Java的命令行程序:“Hello World”。 ```java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } /* 打开终端窗口。 使用 cd 命令将当前目录切换到保存 HelloWorld.java 文件的目录 使用 javac 命令编译 Java 程序 java 命令运行编译后的 Java 程序 */ javac HelloWorld.java java HelloWorld ``` ##### (2)练习数值计算。 找出一个整数数组中子数组之和的最大值,例如:数组[1, -2, 3, 5, -1],返回8(因为符合要求的子数组是 [3, 5]);数组[1, -2, 3, -8, 5, 1],返回6(因为符合要求的子数组是 [5, 1]); 数组[1, -2, 3,-2, 5, 1],返回7(因为符合要求的子数组是 [3, -2, 5, 1])。 ```python def max_subarray_sum(nums): # 初始化maxSum和currentSum为数组第一个元素 maxSum = currentSum = nums[0] for i in range(1, len(nums)): # 如果currentSum加上当前元素大于当前元素,则更新currentSum为currentSum + 当前元素。 # 否则,更新currentSum为当前元素。 currentSum = max(currentSum + nums[i], nums[i]) # 如果currentSum大于maxSum,则更新maxSum为currentSum。 maxSum = max(maxSum, currentSum) return maxSum # 获取用户输入的整数数组 nums = input("请输入整数数组(用英文逗号分隔):").split(",") nums = [int(x) for x in nums] # 输出最大子数组和 print(max_subarray_sum(nums)) ``` ##### (3)写一个命令行程序, 要求:输出1~20000内的所有素数,按每行5个打印出来,并分析程序中最费时的函数是什么, 如何改进? ```python # 判断一个数是否为素数函数 def is_prime(n): if n <= 1: return False for i in range(2, n): # 如果当前的数 i 能够整除给定的数 n,则 n 不是素数 if n % i == 0: return False # 循环结束后没有找到能整除 n 的数,则说明 n 是素数 return True # 输出素数函数 def print_primes(): count = 0 # 遍历1~20000之间所有的数字 for num in range(1, 20001): if is_prime(num): print(num, end=" ") count += 1 # 5个为一行打印 if count % 5 == 0: print() print_primes() ``` 在这段代码中,最费时的函数是`is_prime(n)`函数。这是因为在判断一个数`n`是否为素数时,使用了一个循环从2到`n-1`进行遍历,对每一个数字都进行取余运算。当数字很大时,这个循环会执行很多次,导致程序的执行时间增长。 \# 优化时可以考虑只判断从2 到 sqrt(i) 是否存在能整除i的数,时间复杂度可以改进为O(sqrt(n))。 ### **软件工程实验二:**原型工具的学习和实践**** **(1)** ***\*系统主界面(商品浏览页)\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps1.jpg) ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps2.jpg) **(2)** ***\*用户登录\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps3.jpg) **(3)** ***\*用户注册\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps4.jpg) **(4)** ***\*购物车\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps5.jpg) **(5)** ***\*下单\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps6.jpg) **(6)** ***\*结算\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps7.jpg) **(7)** ***\*支付\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps8.jpg) **(8)** ***\*库存\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps9.jpg) **(9)** ***\*添加商品\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps10.jpg) **(10)** ***\*留言板\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps11.jpg) **(11)** ***\*相关信息\**** ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps12.jpg) ### 软件工程实验三:面向对象的系统建模A #### ***\*A1-1\**** 用Thoroughbred类对优良品质的马进行建模。它有3个属性:mother,father,birthyear,还有3个操作:getCurrentAge()、getFather()和getMother。 每个属性都有名字、类型和可见性级别。类型和可见性都是可选的。类型放在名字后面,并用冒号进行分隔。可见性由前面的-、#、或+指定,分别代表私有、受保护、包或公有可见性。在实验中,所有属性都是私有的,由前面的减号(—)指出。可以用可见性级别、带名字和类型的参数以及返回类型来表示每个操作。 ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps13.png) 用Java实现Thoroughbred类: ```java public class Thoroughbred { private String mother; private String father; private int birthyear; public Thoroughbred(String mother, String father, int birthyear) { ​ this.mother = mother; ​ this.father = father; ​ this.birthyear = birthyear; } public int getCurrentAge() { ​ return birthyear; } public String getFather() { ​ return father; } public String getMother() { ​ return mother; } } ``` #### ***\*A1-2\**** College有一个包含Building对象的聚合,这表示建筑构成了学院。学院也有一个包含课程的组合。Course类也受到约束性注释(注释Course一定在某个Building中进行)。 ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps14.png) ```java class Building { private String name; public Building(String name) { ​ this.name = name; } public String getName() { ​ return name; } } class Course { private String name; public Course(String name) { ​ this.name = name; } public String getName() { ​ return name; } } class College { private List buildings; private List courses; public College() { ​ buildings = new ArrayList<>(); ​ courses = new ArrayList<>(); } public void addBuilding(Building building) { ​ buildings.add(building); } public void addCourse(Course course) { ​ courses.add(course); } public List getBuildings() { ​ return buildings; } public List getCourses() { ​ return courses; } } ``` #### ***\*A1-3\**** 一个学院School由1到多个系Department构成;每个系至少拥有1位指导教师Instructor,并提供至少1门课程Subject;每个指导教师至少属于某个系且教授1-3门课程;学院拥有0到多名学生,学生Student参加1-5门课程学习。请使用UML描述以上各类间的关系。 ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps15.png) ```java class School { private List departments; private List students; public School() { ​ departments = new ArrayList<>(); ​ students = new ArrayList<>(); } public void addDepartment(Department department) { ​ departments.add(department); } public void addStudent(Student student) { ​ students.add(student); } } class Department { private List instructors; private List subjects; public Department() { ​ instructors = new ArrayList<>(); ​ subjects = new ArrayList<>(); } public void addInstructor(Instructor instructor) { ​ instructors.add(instructor); } public void addSubject(Subject subject) { ​ subjects.add(subject); } } class Instructor { private String name; private List subjects; public Instructor(String name) { ​ this.name = name; ​ subjects = new ArrayList<>(); } public void addSubject(Subject subject) { ​ subjects.add(subject); } } class Student { private List subjects; public Student() { ​ subjects = new ArrayList<>(); } public void addSubject(Subject subject) { ​ subjects.add(subject); } } class Subject { private String name; public Subject(String name) { ​ this.name = name; } } ``` #### ***\*A1-4\**** 简化的网上购物系统需求说明:客户(Customer)具有四个属性:姓名(name),联系电话(contact),邮寄地址(deliveryAddress)和是否激活状态(active),订单(Order)具有两个属性:创建日期(createDate),订单状态(status)包括CREATE、PAID、SHIPPING、DELIVERED、CANCEL五种状态;订单详情(OrderDetail)具有一个属性:数量(quantity),并具有calculateSubTotal()、calculateWeight()方法;商品项(Product)具有名称(title)、重量(weight)和描述(description),并具有getPriceForQuantity()方法和getWeight()方法;支付方式(Payment),具有金额(amount)属性;支付方式支持以下五种支付方式:信用卡Credit,具有卡号(number)、类型(type)和过期日期(expireDate);现金(Cash),具有支付金额(cashTendered)属性;在线转账(WireTransfer),具有银行ID:bankID,银行名称bankName属性,支付宝(AliPay)具有卡号number属性;微信支付(WeixinPay),具有卡号属性。请根据以上简化的网上购物系统的关键类描述说明,创建类模型,并定义类之间的关系包括多重性(Multiplicity)。 ![img](file:///C:\Users\Kky6-Win\AppData\Local\Temp\ksohtml29360\wps16.png) ```java class Customer { private String customerId; private String name; private List orders; public Customer(String customerId, String name) { ​ this.customerId = customerId; ​ this.name = name; ​ this.orders = new ArrayList<>(); } public void addOrder(Order order) { ​ orders.add(order); } } class Order { private String orderId; private Customer customer; private List orderDetails; private Payment payment; public Order(String orderId, Customer customer) { ​ this.orderId = orderId; ​ this.customer = customer; ​ this.orderDetails = new ArrayList<>(); } public void addOrderDetail(OrderDetail orderDetail) { ​ orderDetails.add(orderDetail); } public void setPayment(Payment payment) { ​ this.payment = payment; } } class OrderDetail { private Product product; private int quantity; public OrderDetail(Product product, int quantity) { ​ this.product = product; ​ this.quantity = quantity; } } class Product { private String productId; private String name; public Product(String productId, String name) { ​ this.productId = productId; ​ this.name = name; } } class Payment { private String paymentId; public Payment(String paymentId) { ​ this.paymentId = paymentId; } } class Credit extends Payment { private String cardNumber; public Credit(String paymentId, String cardNumber) { ​ super(paymentId); ​ this.cardNumber = cardNumber; } } class Cash extends Payment { private double amount; public Cash(String paymentId, double amount) { ​ super(paymentId); ​ this.amount = amount; } } class WireTransfer extends Payment { private String bankAccount; public WireTransfer(String paymentId, String bankAccount) { ​ super(paymentId); ​ this.bankAccount = bankAccount; } } class AliPay extends Payment { private String account; public AliPay(String paymentId, String account) { ​ super(paymentId); ​ this.account = account; } } class WeixinPay extends Payment { private String account; public WeixinPay(String paymentId, String account) { ​ super(paymentId); ​ this.account = account; } } ``` ### 软件工程实验四:面向对象的系统建模B #### *B1* 绘制恒温水箱20°~100°的状态图。恒温水箱启动之后,当温度传感器检测温度低于20°时,如果水量传感器检测到水箱中有水,则打开继电器电源,开始烧水;如果水量传感器检测到当前无水,则不打开电源烧水。当温度传感器检测到水箱中水的温度达到100°,则断开电源。如果水箱烧坏,则进行维修。当晚上11点整时,自动断开电源,进入休眠状态;早上7点整,自动进入上述恒温烧水过程。 ```java public class WaterHeater { private State state; public WaterHeater() { this.state = new StandbyState(); } public void setState(State state) { this.state = state; } public void start() { state.start(this); } public void detect() { state.detect(this); } public void heat() { state.heat(this); } public void powerOff() { state.powerOff(this); } public void repair() { state.repair(this); } } public interface State { void start(WaterHeater waterHeater); void detect(WaterHeater waterHeater); void heat(WaterHeater waterHeater); void powerOff(WaterHeater waterHeater); void repair(WaterHeater waterHeater); } public class StandbyState implements State { public void start(WaterHeater waterHeater) { System.out.println("Starting..."); waterHeater.setState(new DetectState()); } public void detect(WaterHeater waterHeater) { System.out.println("Cannot detect in standby state."); } public void heat(WaterHeater waterHeater) { System.out.println("Cannot heat in standby state."); } public void powerOff(WaterHeater waterHeater) { System.out.println("Already in standby state."); } public void repair(WaterHeater waterHeater) { System.out.println("Repairing..."); waterHeater.setState(new RepairState()); } } public class DetectState implements State { public void start(WaterHeater waterHeater) { System.out.println("Already started."); } public void detect(WaterHeater waterHeater) { System.out.println("Detecting..."); waterHeater.setState(new HeatState()); } public void heat(WaterHeater waterHeater) { System.out.println("Cannot heat in detect state."); } public void powerOff(WaterHeater waterHeater) { System.out.println("Powering off..."); waterHeater.setState(new StandbyState()); } public void repair(WaterHeater waterHeater) { System.out.println("Repairing..."); waterHeater.setState(new RepairState()); } } public class HeatState implements State { public void start(WaterHeater waterHeater) { System.out.println("Already started."); } public void detect(WaterHeater waterHeater) { System.out.println("Cannot detect in heat state."); } public void heat(WaterHeater waterHeater) { System.out.println("Heating..."); } public void powerOff(WaterHeater waterHeater) { System.out.println("Powering off..."); waterHeater.setState(new StandbyState()); } public void repair(WaterHeater waterHeater) { System.out.println("Repairing..."); waterHeater.setState(new RepairState()); } } public class RepairState implements State { public void start(WaterHeater waterHeater) { System.out.println("Cannot start in repair state."); } public void detect(WaterHeater waterHeater) { System.out.println("Cannot detect in repair state."); } public void heat(WaterHeater waterHeater) { System.out.println("Cannot heat in repair state."); } public void powerOff(WaterHeater waterHeater) { System.out.println("Powering off..."); waterHeater.setState(new StandbyState()); } public void repair(WaterHeater waterHeater) { System.out.println("Already in repair state."); } } ``` WaterHeater类代表恒温水箱,State接口定义了所有可能的状态转换,每个状态都是State接口的一个实现。当恒温水箱的状态改变时,需要调用WaterHeater的setState方法来改变其内部的state字段。 #### *B2* 阅读教材《软件工程-实践者的方法》第八版 SafeHome项目说明(教材122页第八版,教材102页第九版),针对控制面板ControlPanel的状态建模,绘制ControlPanel状态图。SafeHome控制面板的行为说明参考如下: 在SafeHome的安全功能中控制面板类ControlPanel的状态如下: 户主敲击控制面板的键盘按键输入密码时,控制面板处于读取键入字符状态。每输入一个字符则执行输入密码与设置的密码长度(4位)比较,如果达到密码长度则验证密码是否正确;如果输入密码错误3次,则锁定控制面板;锁定时间超过120秒之后,再次允许户主输入;如果输入的密码正确,控制面板进入系统功能选择状态。 ```java public class ControlPanel { private static final int MAX_ATTEMPTS = 3; private static final int LOCK_TIME = 120; private static final String PASSWORD = "1234"; private enum State { READY, LOCKED, FUNCTION_SELECT } private State state; private int attempts; private long lockStart; public ControlPanel() { state = State.READY; attempts = 0; } public void enterPassword(String input) { if (state == State.LOCKED) { if ((System.currentTime() - lockStart) > LOCK_TIME) { state = State.READY; } else { System.out.println("控制面板已锁定"); return; } } if (input.equals(PASSWORD)) { state = State.FUNCTION_SELECT; System.out.println("密码正确,进入系统功能状态"); } else { attempts++; if (attempts >= MAX_ATTEMPTS) { state = State.LOCKED; lockStart = System.currentTimeMillis(); System.out.println("密码错误,控制面板已锁定"); } else { System.out.println("密码错误,请重新输入"); } } } public void selectFunction() { if (state == State.FUNCTION_SELECT) { System.out.println("选择功能"); } else { System.out.println("请先输入密码"); } } } ``` 枚举State,表示控制面板的状态。enterPassword方法用于输入密码并处理状态转换。如果控制面板被锁定,它会检查是否已经过了锁定时间。如果密码正确,控制面板会进入功能选择状态。如果密码错误,它会增加尝试次数,如果尝试次数达到最大值,控制面板会被锁定。如果控制面板没有在功能选择状态,它会提示用户先输入密码。 #### *B3* 根据以下问题描述,采用状态图/状态机建模,并实现之: 用户需要从源代码文件中提取源文件中的所有字符串。字符串是双引号之间的文本,但是字符串中的反斜杠会转义下一个字符,比如对于字符串:”一般学习一门编程语言的时候,基本上第一个程序是输出\”Hello World!\””,应该输出:一般学习一门编程语言的时候,基本上第一个程序是输出”Hello World!”。 ```java public class StringExtractor { private enum State { NORMAL,TRANSFER } public List extractStrings(String source) { List strings = new ArrayList<>(); StringBuilder currentString = new StringBuilder(); State state = State.NORMAL; for (char c : source.toCharArray()) { switch (state) { case NORMAL: if (c == '\') { state = State.TRANSFER; } else if (c == '"') { if (currentString.length() > 0) { strings.add(currentString.toString()); currentString = new StringBuilder(); } } else { currentString.append(c); } break; case TRANSFER: currentString.append(c); state = State.NORMAL; break; } } return strings; } } ``` extractStrings方法,它接收源代码字符串,然后遍历每个字符,根据当前状态和字符来决定下一步的操作。如果状态是NORMAL并且字符是反斜杠,那么状态变为TRANSFER。如果字符是双引号,那么结束当前字符串并开始一个新的字符串。如果状态是TRANSFER,那么无论字符是什么,都添加到当前字符串,并将状态变回NORMAL。最后,返回所有提取的字符串。