1.前言
经过这两次对家具强电电路模拟的迭代,我对JAVA编程语言有了更深入的理解,同时在程序开发设计环节也有了很大的进步,吸收了上次编程练习的教训后,我在本阶段的编程练习中也有针对性的进行了改进。
本次的JAVA练习内容丰富,包括各种数据结构的应用,如哈希表,列表,数组等;迭代器的使用;创建有序集合;正则表达式的运用;类关系的应用,如继承关系以及抽象类和抽象函数的复写问题;类设计,包括函数重载问题等。而主要难点在于这两次练习的逻辑设计和代码运行流程上。
这次的两个JAVA练习难度上依旧是由浅入深,难的部分主要集中在每次迭代会新增元器件,要使新增元器件与原本的电路相适应,同时,迭代还会对电路状态判定产生新的要求,也需要对原有的结构进行修改,本次我主要针对这两个问题详细阐述我在编程中的设计和遇到的问题。
2.设计与分析
2.1 题目集7
在本次迭代中,新增的要求一个是增加一个互斥开关,互斥开关有三个接口,每次只有两个接口相连,同时互斥开关不同接口相连时产生的电阻不同。第二个新增元器件是受控窗帘,受控窗帘需要根据两端电压决定是否启动,然后根据整个电路产生的亮度决定打开比例,因此在更新受控窗帘状态之前,还需要对整个电路的亮度进行分析。第三个变化是,主干路中允许多个并联电路串联在一起。
首先,最好解决的是第三个要求,干路中允许多个并联电路串联在一起。在上一次迭代中,我们已经有了更新并联电路的函数:
public void updateParallel(String id, double volt);
只要在更新串联电路时,增加一个判定元件是否为并联电路的判定即可。
if (temp1.startsWith("M")) {
double resis = parallelResistance(temp1);
Double vol = volt * resis / totalresis ;
updateParallel(temp1, vol);
}
其次,解决第二个问题,在设计受控窗帘类的时候,我为它新增了两个指标,一个是亮度,另一个是打开比例,在主干路更新过程中,如果遇到受控窗帘,那么就记录受控窗帘的压降,先不进行更新,而是用continue跳过,在更新完干路的所有设备以后,遍历devices列表,先计算系统总亮度,用总亮度对受控窗帘的参数进行更新,进而更新其打开比例,具体设计如下:
class Curtain extends ControlledDevice{
protected double open; //打开比例
protected double lightness;
public Curtain(String id, String number) {
super(id, number, 15.0); // 窗帘的电阻为15
this.open = 1.0; //默认为全开状态
this.lightness = 0.0;
}
public void updateLightness(double light);
public void updateState(double inputVoltage);
@Override public String getStatus() ;
}
最后解决互动开关问题,互动开关存在几个要点:首先,互动开关对2号引脚和三号引脚存在选择性,需要对引脚转换进行捕捉和修改;其次,互动开关的引脚信息是重要的,因此需要在输入的时候保留引脚信息。
首先解决互动开关类的设计问题,互动开关和开关类似,都属于控制元件,因此互动开关需要继承控制元件类。为了捕获互动开关连接的引脚信息,我为互动开关设计了一个布尔型参数on,on=true时,表示互动开关12引脚相连,on=false时,表示互动开关13引脚相连,与开关类似,使用toggle()函数对开关状态转换进行修改,除了修改引脚,还需要修改电阻,因为12和13相连产生的电阻不一样。
class SPDTswitch extends ControlDevice{
protected boolean on; // 开关状态
public SPDTswitch(String id, String number){
super(id, number);
this.on = true; // 初始化为1-2引脚相连
this.resistance = 5.0;
}
public void toggle() ;
@Override public void updateState(double inputVoltage) ;
@Override public String getStatus();
}
其次是互动开关的引脚问题,在之前的迭代中,我们并不关注设备接入的引脚顺序,因为这是多余的,对设备的状态更新没有很多实质性的作用,反而会造成存储上的冗余,还会造成代码量过大,代码过长,判定过程过于复杂等情况。但是在互动开关上,由于它存在三个引脚,因此,如果不存储引脚信息,那么我们在更新设备信息时将无法判定互动开关的连接状态是怎样的,包含互动开关的支路到底是联通还是断开,因此需要在处理输入信息的时候处理互动开关的引脚。同时,之前我们在进行输入设备的存储时,只关注后端的设备,因为这样就已经足够了,但是在这个情况中,由于互动开关有三个引脚,而且我们关心引脚连接的先后顺序,因此,我们需要判定前端的设备是否还是互动开关,如果是,仍需要存储。
if(endpoints[0].startsWith("H"))
connectionList.add(endpoints[0]);
if(to.startsWith("H")) {
connectionList.add(endpoints[1]);
continue;
}
在解决了互动开关设计和存储的情况之后,需要解决互动开关在实际电路中存在的问题。首先是互动开关在判断电路状态上的影响。在串联电路中,如果遇到互动开关,需要根据电路中存储的引脚和互动开关连接的引脚是否匹配,如果串联电路中存储的互动开关引脚为2,但实际上13引脚相连(或者存储的是3,但是12引脚相连),那么这叫支路会被判定为断路,如果匹配成功,才继续判断支路中的其他元器件。
由于在互动开关存储的时候存储了引脚信息,而其他设备都没有存储引脚信息,因此在每一个需要遍历支路设备的函数和遍历过程中,都要注意提取设备时要把引脚信息去掉才可以提取出设备,否则会产生空指针问题,这也是我在多次调试代码中遇到的问题。
2.2 题目集8
本次迭代相较于上一次新增了如下内容:
1.新增设备二极管,二极管具有正向导通,反向截止的特性,因此在获取设备信息的时候需要先判定二极管的状态;
2.在并联电路中允许出现并联电路
3.在输出设备信息时,要同时输出设备的引脚电压,还要对流经设备的电流大小进行判定,要对超过设备极限电流的情况进行提示,如果电路出现了电流无穷大,可能烧坏电路的情况,需要给出提示,并不再输出设备信息。
接下来我将逐步介绍解决这三个问题的设计。首先是二极管的设计问题,二极管首先作为受控元件,但是作用和开关非常类似,因此我们可以类比开关设计。二极管继承于受控元件,但是由于具有正向导通(类似于开关闭合)和反向截止(类似于开关断开)的特性,我们为其设计一个布尔型参数on,on=true表示二极管正向导通,on=false表示二极管反向截止。另外,由于二极管的具有类似开关的性质,我们为其设计了一个toggle()函数,当二极管反向截止时,修改on和resistance参数。下面是二极管的类设计:
class DiodeTube extends ControlledDevice{
protected boolean on;
public DiodeTube(String id, String number) {
super(id, number, 0.0); // 正向导通电阻为0
this.on = true; //默认二极管是正向导通的
}
public void toggle();
@Override public void updateState(double inputVoltage,double outputVoltage);
@Override public void updateState(double inputVoltage,double outputVoltage , double current);
@Override public String getStatus() ;
}
其次是并联电路包含并联电路问题,这个问题实际上不需要额外进行迭代更新,因为在上一次迭代中,我已经允许在串联电路的更新过程中允许并联电路的出现,因此本次迭代不需要再对这个条件进行更新。
最后是相对比较难处理的引脚电压问题。之前已经说过,我在进行设备创建和存储的时候并没有存储引脚信息,但是最后又要按序输出引脚电压;其次,在前面的更新中,只需要知道设备两端的压降即可对设备状态进行更新,如果还是只用压降,那么我们就无法得知具体的输入电压和输出电压信息;第三,元器件处于断路支路中,短路支路中和联通支路中的更新情况不一样。
那么针对这三个问题,我在类设计上做了比较大的变动。首先是引脚信息问题,如果我将所有设备的引脚信息都存下来,这实际上是没有必要的,因为总干路总是从VCC开始,那么我只要知道这个设备第一次出现时的引脚是1还是2就可以了。因此我为设备创建了一个isNearVcc的布尔型参数,true表示1号引脚靠近VCC,false表示1号引脚靠近GND,在输出设备的两个引脚电压时,如果isNearVcc是true,那么输出就是inputVoltage-outputVoltage,否则就是outputVoltage-inputVoltage,这样,第一个问题就迎刃而解了。而改变这个参数的方法我写在changeDirection()函数中,用于在处理设备信息的函数中调用,如果第一次捕获到的设备引脚是1,就不调用,是2就调用函数改变isNearVcc。而针对互动开关,由于互动开关有三个引脚,而第一个引脚出现的位置可能是前方,也可能是后方,因此,没有在处理设备和存储设备的时候就对互动开关的方向进行判定,而是全部存储完了以后,在更新设备的第一步才对其进行方向判定。
public void judgeSPDTswitch();
其次是压降,如果只有压降不行,那么我就把输入电压和输出电压作为参数传参更新,压降就是输入电压和输出电压的差,这样虽然多了一个函数参数,但却是不可避免的。第三是元器件所处支路的状态问题,对于这个问题一开始我的设计是给updateState()函数再增加一个参数,根据参数不同进行不同的更新,但是我认为这样会导致函数过长,同时新增的参数在调用之前还需要经过一系列的判定才能得出,因此我放弃了这种想法。查阅资料后我采用的方法是使用函数重载法,由于断路中没有电流,因此可以少传一个参数,那么利用参数个数差异创建updateState的重载函数在这个情况下将适应的很好。下面就是我对基类的重新设计,后续的子类也一一作了修改。
abstract class CircuitDevice {
protected String id; // 设备标识符(例如:K4)
protected String number; // 设备编号
protected double inputVoltage; // 输入电压
protected double outputVoltage; // 输出电压
protected double resistance; // 电阻
protected double current;//电流
protected boolean isNearVcc; //1号引脚靠近开关还是地
public CircuitDevice(String id, String number, double resistance); //构造函数,内容略
public abstract void updateState(double inputVoltage , double outputVoltage); //如果是断路,只需要对电压进行更新,没有电流通过
public abstract void updateState(double inputVoltage , double outputVoltage , double current); //如果是正常联通电路或者短路,添加电流参数即可
public abstract String getStatus();
public String getId();
public String getNumber();
public double getOutputVoltage();
public void setInputVoltage(double inputVoltage);
public double getResistance();
public void changeDirection() ;
}
在修改完基类以后,下一步就是对设备状态进行更新,基本的策略是,如果总电路正常联通,那么就按照之前的设计更新,如果总电路短路,那么提示短路,结束程序,如果总电路断路,那么需要找到第一个断开的元器件,用inputVoltage-outputVoltage对其进行更新,在此之前的元器件用inputVoltage-inputVoltage更新,在此之后的元器件用outputVoltage-outputVoltage更新。
基于这样的策略,在更新过程中主要遇到的问题是大回路中包含小回路的情况,具体来说就是在并联电路中,如果存在某条支路断路,并不意味着支路的元器件引脚电压都为0,有可能需要用并联电路的输出电压对元器件进行更新,第二个问题是多断点问题,如果支路中的控制元件有很多,比如存在多个开关断开或者二极管反向截止问题,当前面的开关断开,而后面还有开关或者二极管的话,需要将输入电压置为0,以防存在多个控制元件的情况,还需要对已经更新过的控制元件进行计数,直到所有的控制元件都更新完。第三个是电流问题,在并联电路更新的时候,由于并联电路的分流问题,需要对用于支路更新的电流进行分流,值得注意的是,如果并联电路短路,那么所有的短路电路也是均分流经并联电路的总电流的。解决这三个问题之后大部分的电路更新基本迎刃而解。
public int checkParallel(String id);
public int checkSeries(String id, boolean type); //true只检查元器件,false检查断开的元器件
public boolean updateDevices() ;
3.练习心得
3.1 电压更新问题
在上一次迭代过程中,在干路我首先便利了一遍干路设备,更新了输入电压后,计算出了干路的总电流,然后又将输入电压设为220.0V,然后再次遍历设备,这样做实际上非常冗余,显得之前的操作十分鸡肋,因为分档调速器和连续调速器只会在VCC之后出现。修改过后,我先用一个循环遍历干路设备,只对分档调速器和连续调速器进行识别,如果存在,那么就更新输入电压(即电路总电压),同时把设备的状态一并更新了。这样就得到了总电压和总电流,而在后续的其他设备更新中,如果遇到了分档调速器或者连续调速器,直接用continue跳过即可。
3.2 电路完整性问题
在输入的时候,如果存在IN、OUT、VCC或者GND有所缺失的情况,实际上表示输入信息不完整,无法更新,之前我默认输入没有问题,输入信息完整,没有考虑过这个问题,经过思考,我认为这个问题不能忽略。一开始我在电路类Circuit中设计了一个布尔型变量hasINandOUT来处理缺乏这四者的问题,但是我发现这样是不够的,因为只有四个参数同时存在这个变量才可以改,但是这样的话就需要更多的变量来辅助记录这个变量,会产生大量无用或者低效用的变量和数据。经过思考,我采用的是在输入的时候,对一整行输入进行判定,当这行输入同时包含IN和OUT或者同时包含VCC和GND的时候,才开始处理输入信息,这样残缺的电路和设备信息就不会被记录,后续就不会出现问题了。
3.3 互动开关的引脚电压
由于互动开关有三个引脚,但是互动开关的性质决定了有一条路是断路,因此一开始我认为可以通过1次更新就解决互动开关的3个引脚问题,但是由于传参的时候只有两个参数,断开的引脚电压与这两个参数可能都不想等,于是我修改成了2次更新,我还设计了一个全局参数,规定对互动开关的修改次数不得超过2次。但是这样依然存在问题,因为每次修改都对3个引脚电压全部更改了,可能原本正确的电压后续又被改错了。最终我决定放弃原本的updateState()函数,再次使用函数重载,将引脚号也作为参数传递,每次只修改两个引脚,下面是其中一个例子:
public void updateState(String pin, Double inputVoltage, double outputVoltage) { //断路
if(pin.contains("2") && isNearVcc) {
this.inputVoltage = inputVoltage;
this.output2 = outputVoltage;
}else if(pin.contains("2") && !isNearVcc) {
this.inputVoltage = outputVoltage;
this.output2 = inputVoltage;
}else if(pin.contains("3") && isNearVcc) {
this.inputVoltage = inputVoltage;
this.output3 = outputVoltage;
}else if(pin.contains("3") && !isNearVcc) {
this.inputVoltage = outputVoltage;
this.output3 = inputVoltage;
}
}
3.4 函数重载
在Java编程语言中,函数重载是指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。参数列表的不同可以表现为参数的个数不同、参数的类型不同,或者参数的顺序不同。在Java中,返回类型不是方法重载的区分条件。也就是说,仅通过改变返回类型不能实现方法重载。Java的函数重载是一种强大的特性,允许在同一个类中定义多个同名方法,只要它们的参数列表不同。这可以提高代码的灵活性和可读性。在本次迭代中,我两次采用了函数重载,如前文所说,1次是利用参数个数不同,重载updateState,使其实现断路和通路设备更新,第二次是在更新互动开关的时候,利用参数类型和个数不同,重载updateState,实现引脚电压的分别更新。
3.5 短路判定
在串联电路的状态判定中,有三种,一种是无电阻联通(短路),一种是有电阻联通,还有一种是断路。在判定最初默认的状态时有电阻联通,但是随着代码的迭代,电路中设备量增加,电路复杂性也在提高,因此状态判定出现了失误。有两种解决办法,一种是在结果不准确的情况下用电阻进行辅助判定,如果串联电阻为0,即使判定结果是有电阻联通,那么也视为短路,方法如下:
if (status == 1 || totalresis == 0)
第二种解决办法是,在串联电路判定函数中新增一个整型变量相对电阻,这个相对电阻不需要知道具体电阻是多少就可以判断是否短路,即在遍历过程中如果遇到有电阻元件,那么相对电阻就自加,如果最后相对电阻不为0,说明电路是有电阻通路,否则电路短路,方法如下:
int resis = 0; //表示相对电阻,不需要精确知道电阻是多少
if(status==0 && resis ==0 ) status=1;
3.6 字符串判定问题
之前已经说过,只有互动开关在存储的时候保留了引脚信息,那么在取出和判定的时候就需要严格按照标准。举个例子来说,我在实操的过程中发现,将"H1-1"按"-"分割,分别存放到part[]数组中,匹配part[1].contains("1")和part[1]=="1"结果可能是不同的,而且也无法在程序中具体推测出发生了什么问题,因为有时是对的,而有时是错的。那么解决办法也很好想,就是把数字的String类型转化为int类型,但是这个时候又发生了错误,因为part[1]可能不仅仅是"1",因此在转换成数字的时候可能出现问题,但是好在引脚只会发生1/2/3这三种可能,因此最终我采用的方法是统一使用contains方法。
4.改进建议
4.1 互动开关
互动开关的改进上主要是在存储的时候是否有必要存储1号引脚。我们知道互动开关实际上是有方向性的,1号引脚既可以输入,也可以输出,那么很容易得出结论,1号引脚是有必要存储的。这个问题实际上是由训练集7引发的,由于在训练集7中,1号引脚只作为输入引脚,因此,实际上对1号引脚的存储是多余的。但是当时我设计的时候并没有意识到这一点,只是认为多存了1个引脚而已,不会对程序产生过多的负面效应。但是在训练集8中,1号引脚就发生了方向逆转,这个时候1号引脚在存储时与2/3引脚的相对位置就产生了至关重要的作用。综上所述,1号引脚是要存储的,但是在遍历到1号引脚的时候,通常不会做过多处理。
4.2 受控窗帘设计
前面我们提到过,受控窗帘需要根据两端电压和系统总亮度来决定打开比例,因此在设计的时候给受控窗帘增加了亮度参数。经过思考后我发现这个参数是可以省略的,那么如何对受控窗帘进行设备更新呢?采用函数重载。将原本的更新函数弃之不用,将亮度作为参数传递到更新函数,同样可以实现受控窗帘的设备更新。这样做的好处是,更贴近现实,因为我们知道窗帘本身没有亮度属性,亮度只是一个外界因素,去掉以后实现了对受控窗帘类的优化。
4.3 短路警告
在训练8中,如果电路中电流太大,需要输出短路警告,同时不再输出设备信息,我采取的措施是将原本的updateDevices函数由void改成boolean,通过向输出函数返回一个boolean型参数,告诉输出函数要不要继续输出设备信息。这样做的好处是函数仍然只有一个输出接口,坏处是多了一些变量和判定,扩大代码的复杂性。通过查阅资料,我发现有一个方法可以让函数正常退出,这样就不需要进行判定,也不用修改原有的输出函数和updateDevices函数。方法如下:
System.exit(0); // 正常退出程序
4.4 电流参数
在训练8中,程序要求对流经设备的电流进行判定。对于无电阻元件(开关,二极管,调速器等),他们无法自行计算电流,因此依赖于外界的传参,但是对于有电阻元件(吊扇,白炽灯,落地扇等),他们通过两端电压就可以计算出电流,因此不依赖于外界电流。那么就可以忽略更新函数中传来的电流参数,自行计算电流,这样做的好处是更准确。由于外界电流是先经过分压,可能还经过分流等一系列操作后传递过来的,在多重修改下很可能不准确,影响了最终的判断,而自己计算的电流在分压和电阻上存在的误差极小,可以大大提高准确度。
5.总结和感悟
经过这几次的JAVA练习,我对程序设计和JAVA编程有了更深刻的理解。在程序逻辑运行方面,在这两次的练习中,由于代码量比较大,且测试点繁多,我在进行代码调整时需要手动运算程序运行结果,亲自运行梳理后才能发现其中的逻辑错误,也让我在代码设计的时候谨慎了很多。然后是编程能力,经过这几次的练习,我的编程能力得到了很大的提高,对设计和逻辑思维有了更深刻的理解。
经过这一学期的JAVA学习,我深感自己在编程技能与逻辑思维方面取得了显著的进步。JAVA,这门强大且通用的编程语言,不仅拓宽了我的技术视野,更激发了我对编程艺术的浓厚兴趣。在技术层面,我掌握了JAVA的基本语法,包括变量、数据类型、运算符、控制结构等基础知识。更重要的是,我学会了如何运用面向对象的思想来设计和实现程序。类的封装、继承、多态等概念,让我对程序的结构有了更深入的理解。此外,我还学习了异常处理、集合框架、输入输出流等高级特性,这些技能无疑为我的编程之路增添了更多的可能。
在学习过程中,我也遇到了不少挑战。有时候,一个简单的语法错误就让我苦思冥想许久;有时候,一个复杂的功能实现让我倍感挫败。但是,正是这些挑战,让我学会了耐心与坚持。我逐渐明白,编程不仅仅是敲击键盘的艺术,更是解决问题的智慧。通过不断地调试与修改,我逐渐培养起了解决问题的能力,这种能力对我来说是无价之宝。
展望未来,我深知自己的编程之路还很长。但是,我相信只要保持对编程的热爱与执着,不断学习与进步,我一定能够在编程的世界里创造属于自己的辉煌。JAVA,这门充满魅力与挑战的编程语言,将成为我未来职业生涯中不可或缺的伙伴。
标签:题目,引脚,double,PTA,开关,电路,JAVA,互动,public From: https://www.cnblogs.com/zhujie-7233/p/18635534