前言
在过去的几周内,我们完成了答题程序-4, 家居强电电路模拟程序-1以及 家居强电电路模拟程序-2的练习,涉及多个知识点和编程技巧。整体来说,这三次题目集共包含了7道题目,题目难度逐步增加。
题量:
答题程序-4(3道题), 家居强电电路模拟程序-1(3道题), 家居强电电路模拟程序-2(1道题)。
难度:
答题程序-4难度较大,该题在答题程序-3上进行功能迭代,这次迭代增加了选择题与填空题,字符串分割难度没有加大,但是答案对比判断的逻辑难度增大。
家居强电电路模拟程序-1的难度相较于上次大作业的难度较小,但由于代码量有所增加,涵盖了更多的知识点,例如继承和多态。要求我们理解如何在面向对象编程中实现继承关系以及如何使用多态来设计更加灵活和扩展性强的程序结构。这部分内容的学习对于我们理解OOP(面向对象编程)原则至关重要,尤其是在模拟家居强电电路的过程中,如何通过对象之间的关系来模拟电路的不同部分。
设计与分析
答题判题程序-4
设计
思路
首先这次大作业相较于上次大作业迭代了填空题与选择题,所以题目的类型由一种增加到了三种,所以我们需要用一个变量type去标识该问题的类型,以及答案判断后分为三种情况,全对,部分正确,错误,所以我们不能像之前那样用boolen类型返回判断答案,我们可以用一个int类型的变量,存储答案比对后的结果,例如0代表错误,1代表部分正确,2代表全部正确。
类图
用户使用答题程序-4顺序图
分析
报表数据
从报表数据可以得知,除 Avg Depth,Max Complexity偏高外其它各项基本正常。
题目类
1 class Question { 2 String content; // 题目内容 3 String answer; // 正确答案 4 boolean status; 5 int qsId; 6 int type; 7 Question(int type ,String content, String answer,boolean status,int qsId) { 8 this.content = content; 9 this.answer = answer; 10 this.status = status; 11 this.qsId = qsId; 12 this.type=type; 13 } 14 }
这次大作业相较于上次,在题型上增加了填空题和选择题,因此新增了一个 type
变量,用于区分不同的题目类型。其他变量则保持不变,确保与之前的逻辑兼容,以便系统能够灵活处理不同类型的题目。
答题系统类
1 class QuizSystem { 2 /*定义私有数据*/ 3 4 public void processInput(String input) { 5 if (input.startsWith("#N:")) { 6 processQuestion(input); 7 } else if (input.startsWith("#T:")) { 8 processTestPaper(input); 9 } else if (input.startsWith("#S:")) { 10 //processAnswerSheet(input); 11 paper[tot++] = input; 12 } else if (input.startsWith("#D")) { 13 Delete[dod++] = input; 14 } else if (input.startsWith("#X")) { 15 AddStudent(input); 16 } else if (input.startsWith("#Z")) { 17 processSelect(input); 18 } else if (input.startsWith("#K")) { 19 processVacancy(input); 20 } else { 21 System.out.println("wrong format:" + input); 22 } 23 } 24 25 public void AddStudent(String input) { 26 /*此处省略实现代码*/ 27 } 28 29 public void processQuestion(String input) { 30 /*此处省略实现代码*/ 31 } 32 33 34 public void processSelect(String input) { 35 /*此处省略实现代码*/ 36 } 37 38 public void processVacancy(String input) { 39 /*此处省略实现代码*/ 40 } 41 42 public void processTestPaper(String input) { 43 /*此处省略实现代码*/ 44 } 45 46 public void processAnswerSheet(String input,TestPaper[]Papers,int testPaperCount) { 47 /*此处省略实现代码*/ 48 } 49 50 }
这次对答题系统进行了全面优化,主要通过将输入处理逻辑集中到一个专门的类中,简化了代码结构并提升了代码的可维护性。针对代码中重复性较高的部分,我提取了通用方法进行封装,避免了重复编码,减少了冗余。输出部分也进行了重构,单独创建了一个 Output
类,将原本的输出方法拆分成多个小方法,分别处理不同的输出需求,从而增强了系统的灵活性和扩展性。整体改进使得代码更加简洁、模块化,易于后期维护和优化。
输出类
1 class Output{ 2 /*定义私有数据类型*/ 3 public Output(QuizSystem quizSystem) { 4 /*此处省略代码实现逻辑*/ 5 } 6 public void generateOutput() { 7 /*此处省略代码实现逻辑*/ 8 } 9 10 private void deleteQuestions() { 11 /*此处省略代码实现逻辑*/ 12 } 13 public void DeleteQuestion(String input) { 14 /*此处省略代码实现逻辑*/ 15 } 16 17 private void sortAnswerSheets() { 18 /*此处省略代码实现逻辑*/ 19 } 20 public TestPaper getTestPaperById(String testId) { 21 /*此处省略代码实现逻辑*/ 22 } 23 private int[] calculateScores(AnswerSheet answerSheet, TestPaper testPaper) { 24 /*此处省略代码实现逻辑*/ 25 } 26 27 private void printQuestionResult(Question question, String userAnswer, int result) { 28 /*此处省略代码实现逻辑*/ 29 } 30 31 private void printStudentScore(AnswerSheet answerSheet, int[] scores, TestPaper testPaper) { 32 /*此处省略代码实现逻辑*/ 33 } 34 35 private int calculateTotalScore(int[] scores) { 36 /*此处省略代码实现逻辑*/ 37 } 38 public int check(String userAnswer,String standard,int type) { 39 /*此处省略代码实现逻辑*/ 40 }
在输出类中,我们对上次的输出方法进行了改进,主要修改了判题逻辑。由于这次增加了新的判题结果,即可能出现部分正确的情况,原本使用布尔类型来判断是否正确的方法已经不再适用。因此,我们引入了一个新的变量 result
,用于存储判题结果。具体来说,result
的取值为 0 表示错误,1 表示部分正确,2 表示全部正确。通过这种方式,可以更精确地反映不同的判题情况,避免了原有逻辑的局限性。
其他类与上次大作业的类基本相同。
家居强电电路模拟程序-1
设计
思路
在此次大作业中,我们设计了一个电路设备类来描述所有电路设备的公共特征。这一设计思想基于面向对象的继承和多态机制,使得不同类型的电路设备可以共享相同的基本功能。具体来说,受控设备类和控制设备类分别用于描述受控设备和控制设备的特性,串联电路类则用于表示由多个电路设备串联而成的电路系统。由于受控设备、控制设备以及串联电路都可以看作是电路设备的一种形式,它们可以继承电路设备类的方法,从而实现代码的复用和简化。
此外,我们还设计了一个输出类,用于整理并控制最终的输出逻辑。在输出类中,我们通过调用电路设备类及其子类的相关方法来生成最终的输出结果,确保输出过程清晰且准确。这种结构不仅符合面向对象设计原则,还提高了代码的可维护性和可扩展性,便于日后的功能扩展或修改。
类图
家居强电电路模拟程序-1顺序图
分析
报表数据
从报表数据可以得知,除 Avg Depth,Max Complexity偏高外其它各项基本正常。
设备类
1 abstract class Device { 2 protected String identifier; // 标识符 3 protected String id; 4 public Device(String identifier,String id) { 5 this.identifier = identifier; 6 this.id = id; 7 } 8 9 public String getIdentifier() { 10 return identifier; 11 } 12 public String getId() { 13 return id; 14 } 15 }
每个电路设备都具有唯一的ID和设备类型标识符(identifier),这些特征是电路设备的基本属性。为了统一管理不同类型的电路设备,我们设计了一个抽象父类 Device
,其中包含有参构造方法,用于初始化每个电路设备的ID和类型标识符。该抽象类定义了电路设备的公共行为和属性,而具体的电路设备类(如受控设备类、控制设备类等)则继承该父类,并扩展各自特有的功能和特性。
控制设备类
1 abstract class Control extends Device { 2 protected String status; // 设备编号 3 public Control(String identifier, String id,String status) { 4 super(identifier,id); 5 this.status = status; 6 } 7 8 public abstract String getStatus(); // 每个设备可能会有不同的状态 9 }
在控制设备中,开关、变压器等设备的状态直接影响电路的输出电压,因此我们需要在 Control
类中引入状态管理机制。通过 status
属性来表示设备当前的状态(如开、关、工作档位等),并通过 getStatus()
方法来获取设备的状态。不同类型的控制设备(如开关、变压器)可以根据各自的状态来决定电压输出。例如,开关设备的状态决定电路是否通电,而变压器的状态则影响输出电压的高低。
受控设备类
1 abstract class Controlled extends Device { 2 private int parameter; // 设备的状态(亮度、转速等) 3 4 public Controlled(String identifier, String id) { 5 super(identifier, id); 6 this.parameter = 0; // 默认状态为0 7 } 8 public abstract void updateState(double voltage) ; 9 public abstract String getParameter() ; 10 }
在受控设备类中,灯、风扇等设备的状态(如亮度、转速等)受到电压的影响,因此我们需要在 Controlled
类中通过 updateState()
方法来根据输入的电压调整设备的状态。具体来说,当设备接收到不同的电压时,updateState()
会根据电压的大小更新设备的参数(如调整亮度或转速)。通过 getParameter()
方法,系统可以获取当前设备的状态参数。这样,受控设备能够根据控制设备的输出变化实时调整自身的行为,实现电路系统的动态响应和灵活控制。
电路类
1 class Circuit { 2 /*定义私有数据*/ 3 public void addDevice(Device device) { 4 devices.add(device); 5 } 6 7 public List<Device> getDevices() { 8 return devices; 9 } 10 11 public Device getDeviceById(String deviceId, Class<?> deviceKind) { 12 /*此处省略查找设备逻辑*/ 13 } 14 15 public void sort(){ 16 /*此处省略分类实现逻辑*/ 17 } 18 public void outputStatus() { 19 // 使用列表存储不同类型的设备 20 // 按设备编号排序并输出状态 21 22 23 }
由于此次大作业中只涉及到一条串联电路,所以我就没有设计并联电路类,在本次设计中,我将所有设备集中管理,通过 Circuit
类对不同类型的设备进行统一管理和操作。设备类型包括开关、调速器、连续调节器、白炽灯、荧光灯和吊扇等。为了便于管理,我使用了不同的 List
来存储设备,并通过 sort()
方法对设备按ID进行分类和排序。在 outputStatus()
方法中,根据设备类型输出设备状态或参数,保证了输出的有序性和可读性。此外,通过 addDevice()
方法实现设备的添加,getDeviceById()
方法则用于按设备ID和类型获取特定设备,确保系统的灵活性和扩展性。
测试Main类
1 public class Main { 2 public static void main(String[] args) { 3 // 处理输入信息 4 // 输出所有设备状态 5 } 6 private static void handleDeviceInput(String inputs, Circuit circuit) { 7 // 解析设备输入并添加到电路中 8 } 9 private static Device createDevice(String identifier, String id,Circuit circuit) { 10 /*此处省略创建设备的代码*/ 11 } 12 public static void handleControlInput(String input,Circuit circuit,double voltage) { 13 /*此处省略设置控制设备状态的代码*/ 14 15 } 16 public static void updateControlled(double voltage,Circuit circuit) { 17 /*此处省略更新受控设备的实现代码*/ 18 } 19 }
在测试主类中负责处理用户输入并与 Circuit
类进行交互。用户输入的设备信息通过 handleDeviceInput() 方法解析,根据设备的标识符(如 K
, F
, L
, B
, R
, D
)创建不同类型的设备对象,并将其添加到 Circuit
类中的设备列表中。输入的控制信息(如调节电压等)则通过 handleControlInput()
方法进行处理,并实时更新设备状态。电路的排序、检查开关状态以及输出设备状态的操作在 Circuit
类中完成。通过这种结构,系统实现了对电路中各类设备的动态管理与状态输出。
家居强电电路模拟程序-2
设计
思路
此次迭代主要是引入了电阻的概念,并扩展了电路的管理功能,涉及到设备电阻和并联电路的处理。首先,在 Device
类中添加了 resistance
属性,表示每个设备的电阻值。通过构造方法初始化设备的标识符、ID 和电阻值,确保每个设备都有独立的电阻信息。其次,为了支持电路的并联管理,我们设计了 Circuit
类,继承自 Device
类,并对其进行了抽象,要求每个子类必须实现 getResistance()
方法,用以获取电路中所有设备的电阻值。
在电路的实现中,我们将设备作为父类,区分了不同类型的设备,并且通过电路的组合(如并联电路)来计算总电阻。这为系统提供了更高的灵活性,使得电路中不同设备的电阻能够影响整体电路的性能。
类图
家居强电电路模拟程序-1顺序图
分析
报表数据
从报表数据可以得知,除 Avg Depth,Max Complexity偏高外其它各项基本正常。
下面是部分代码的截取
电路类
1 abstract class Circuit extends Device{ 2 Circuit(String identifier,String id,double resistance){ 3 super(identifier,id,resistance); 4 } 5 abstract public double getResistance(); 6 }
电路类中有串联和并联电路,此次设计通过继承 Device
类并扩展为 Circuit
类,旨在实现电路设备的电阻管理。每个电路子类实现 getResistance()
方法来计算和返回整体电阻,以支持不同电路类型的电阻处理和并联电路的扩展。
串联电路类
1 class SeriesCircuit extends Circuit { 2 List<Device> devices ; 3 int cmd = 0; 4 5 SeriesCircuit(String id, List<Device> devices, double resistance, int cmd) { 6 super("T", id, resistance); 7 this.devices = devices; 8 this.cmd = cmd; 9 } 10 11 public Device getDeviceById(String deviceId, Class<?> deviceKind) { 12 } 13 14 // 更新电路电阻的方法 15 public void updateResistance() { 16 17 } 18 19 @Override 20 public double getResistance() { 21 // 返回最新计算的电阻 22 return this.resistance; 23 } 24 }
在设计串联电路时,依据是否包含电源,我们将电路分为主串联电路和次串联电路。通过引入变量 cmd
来标识电路类型,cmd
=1时为主串联电路时表示主电路,cmd=0时则视为次电路。每个串联电路由多个设备组成,并通过 getDeviceById
方法根据设备ID和类型获取对应设备。电路的总电阻通过 updateResistance
方法动态计算,考虑了设备的状态,特别是开关的状态来调整电阻值,当开关闭合时,开关电阻趋近于零;当开关断开时,电阻趋近于无穷。
并联电阻类
1 class MCircuit extends Circuit { 2 public List<SeriesCircuit> seriesCircuits ; 3 4 MCircuit(String id, List<SeriesCircuit> seriesCircuits, double resistance) { 5 super("M", id, resistance); 6 this.seriesCircuits = seriesCircuits; 7 } 8 9 public void addSeriesCircuit(SeriesCircuit seriesCircuit) { 10 11 } 12 @Override 13 public double getResistance() { 14 return this.resistance; // 返回更新后的并联电路电阻 15 } 16 }
在设计并联电路时,我们将多个串联电路(SeriesCircuit
)组合成一个并联电路(MCircuit
)。每个串联电路有自己的电阻值,MCircuit
类通过 updateResistance
方法计算并联电路的总电阻。并联电路的总电阻是通过计算各个串联电路电阻倒数的和来得到的。方法中使用了总倒数公式:1/R_total = Σ(1/R_i)
,然后计算出总电阻。该电路还支持动态添加串联电路,并能在电阻变化时更新总电阻值。
此次我将输入与输出分开分装成一个类,输入类为CircuitManger用于处理输入信息,输出类为CircuitDevice用于输出设备状态。
其他类与上次大作业除迭代了电阻属性外,其他不变。
踩坑心得
1 boolean contains = standard.contains(userAnswer); 2 if (contains) { 3 return 1; 4 } else 5 return 0;
#T:1 1-5 2-10
#T:2 1-8 2-21
#N:1 #Q:1+1= #A:2
#S:2 20201103 #A:1-2 #A:2-古筝
#S:1 20201104 #A:1-2 #A:2-瑟
#S:1 20201103 #A:1-5 #A:2-瑶琴或七弦琴
#S:2 20201104 #A:1-5 #A:2-琴
#X:20201103 Tom-20201104 Jack
#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
end
输出结果
或者为
#T:1 1-5 2-10
#T:2 1-8 2-21
#N:1 #Q:1+1= #A:2
#S:2 20201103 #A:1-2 #A:2-古筝
#S:1 20201104 #A:1-2 #A:2-瑟
#S:1 20201103 #A:1-5 #A:2-七弦琴或瑶琴
#S:2 20201104 #A:1-5 #A:2-七弦琴
#X:20201103 Tom-20201104 Jack
#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
end
第一个输入的答案是标准答案的子串,但他并不是部分正确的答案,第二个答案虽然不是子串但他却是标准答案,因此后面调整为以或为分割符存储答案,与用户的答案进行比对。
String[] standards=standard.trim().split("或");String[] userAnswers=userAnswer.trim().split("或");if(userAnswers.length>standards.length)return 0;for(int i=0;i<userAnswers.length;i++){for(int j=0;j<standards.length;j++) {if (userAnswers[i].trim().equals(standards[j].trim()))sum1++;}if(sum1==0)return 0;}if(sum1==standards.length)return 2;elsereturn 1;}
坑点2:在处理选择题答案比对时,我采用的是一对一比对,若最后比对答案正确的数量与标准答案个数相同则为全对,
但当测试点为
#N:1 #Q:1+1= #A:2
#Z:2 #Q:党十八大报告提出要加强()建设。A 政务诚信 B 商务诚信 C社会诚信 D司法公信 #A:A B C D
#T:1 1-5 2-9
#X:20201103 Tom
#S:1 20201103 #A:1-5 #A:2- C C C C
end
输出结果为
该答案虽然比对后正确个数与标准答案个数相同,但是其并不是正确答案,后面采用逻辑将相同的答案删除后在进行比对。
int sum = 0;int sum1=0;String[] useAnswer = userAnswer.split(" ");String[] select = standard.split(" ");for (int i = 0; i < useAnswer.length; i++) {if (standard.contains(useAnswer[i])) {sum++;}elsereturn 0;}if (sum == select.length)return 2;else if(sum==0)return 0;return 1;}
坑点3:在家居强电电路模拟程序-1处理分档调速器时没有考虑到最高档位为三,当继续#F+时应该不进行任何处理。
当测试点为
[VCC F1-1]
[F1-2 D2-1]
[D2-2 GND]
#F1+
#F1+
#F1+
#F1+
end
输出为
后面加入大于三挡就不处理的逻辑后,问题得以解决。
坑点4:在家居强电电路模拟程序-2中刚开始时将开关断开电阻视为0,
当测试点为
#T1:[IN K1-1] [K1-2 D2-1] [D2-2 OUT]
#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
#M1:[T1 T2]
#T3:[VCC L1-1] [L1-2 K3-1] [K3-2 M1-IN] [M1-OUT D3-1] [D3-2 GND]
#K1
#K2
#L1:1.00
end
输出结果为
这是因为处理电阻分压时使用了电阻为分母,导致电压为无穷大,从而分压无穷大,D1,D2为转速最大值,后面将开关断开时,电阻改为一个很大的数值,视为无穷大后问题得以解决。
改进建议
在家居强电电路模拟程序-2中在家居强电电路模拟程序中,确实有一些场景可以通过多态来优化代码结构,提高灵活性和可维护性。例如,电路中的各类电阻设备(如串联电路、并联电路等)可以通过继承一个基类并实现各自特定的电阻计算方法,从而避免在不同电路类型中重复编写相似的代码。这种多态设计能够简化代码,使得新增电路类型或修改电路行为时更加方便。,但由于掌握的不够,导致不敢去下手。另外,测试点方面,确实需要考虑并联电路和主串联电路都断开的情况。如果电路的开关全部断开,应该确保程序能正确地处理这种无效电路状态,避免出现电阻值无穷大的错误。比如,如果主串联电路和所有并联电路都断开连接,理论上电路是无法导通的,这个测试点我觉得很有必要,因为,可能在处理的过程中,出现主串联电路电阻无穷大,并联电路电阻也无穷大,导致分压都为电源电压,而不是零的情况。此时,电源电压应该全部分布在电路两端,而不是错误地分压。通过测试这个情境,可以确保程序能处理所有可能的电路状态,并防止因电阻无穷大导致的计算错误或异常输出。