对题集“数字电路模拟程序”三次作业的总结

上次的随笔中我总结了关于航空器配重与货运管理系统的作业总结,而在本次随笔中,我将继续总结新题集---关于数字电路模拟程序的作业总结,反思这三次作业的不足之处。

前言

   在第一次作业中,题给出要求设计基础的电路元件和电子配件,并对基础电子配件做出功能性要求需要我们实现,结果应该来讲是不尽人意的,首先第一个难点就是对于首次接触未给出类图的新手来说,自行设计电子配件类和电路元件类是一个巨大的挑战;第二个难点就是在设计完类后突然发现,对于元件的输入解析又是一大头疼之处,对元件的输入解析,其实就是对于字符串的操作,这显然十分考验编程者对于字符串操作的熟悉程度。

  在第二次作业中,题新增了一个电子配件和三个电路元件,增加了电路配件意味着对于字符串的解析要有更多的情况,无疑也是在考核字符串操作。

  第三次作业取消了第二次作业新增元件,取而代之的是增加子电路这个概念以及异常输入处理,由于题集较难,出题者给予了我们帮助,给出了设计建议。

 

设计与分析


第一次作业

目的:给出电路元件特征,编程实现数字电路模拟程序。

Pin类:负责存储输入的0、1电信号。

class Pin {
    String componentName;
    int pinNum;

    Pin(String fullPin) {
        int i = fullPin.lastIndexOf('-');
        if (i == -1) {
            this.componentName = fullPin;
            this.pinNum = 0;
        } else {
            this.componentName = fullPin.substring(0, i);
            this.pinNum = Integer.parseInt(fullPin.substring(i + 1));
        }
    }
}

Component抽象类:将引脚组合起来,便于题中的5种门继承

abstract class Component {
    String componentName;
    List<Pin> input;
    int num;
    int output;

    Component(String name) {
        this.componentName = name;
        this.input = new ArrayList<>();
        this.output = -1;
        this.num = extractNumber(name);
    }

    private int extractNumber(String name) {
        int i = name.length() - 1;
        while (i >= 0 && name.charAt(i) >= '0' && name.charAt(i) <= '9') {
            i--;
        }
        if (i + 1 >= name.length()) {
            return 0;
        }
        return Integer.parseInt(name.substring(i + 1));
    }

    void addPin(Pin pin) {
        input.add(pin);
    }

    String getName() {
        return componentName;
    }

    abstract int analysis(List<Integer> list);
}

AndGate类:继承Component,包含两个或多个输入引脚和一个输出引脚。所有输入引脚必须都是高电平,输出引脚才是高电平,只要有一个输入引脚为低电平,输出引脚输出低电平。

class AndGate extends Component {
    AndGate(String name) {
        super(name);
    }

    @Override
    int analysis(List<Integer> list) {
        for (int v : list) {
            if (v == 0) return 0;
        }
        return 1;
    }
}

OrGate类:继承Component,包含两个或多个输入引脚和一个输出引脚。所有输入引脚必须都是低电平,输出引脚才是低电平,只要有一个输入引脚为高电平,输出引脚输出高电平。

class OrGate extends Component {
    OrGate(String name) {
        super(name);
    }

    @Override
    int analysis(List<Integer> list) {
        for (int v : list) {
            if (v == 1) return 1;
        }
        return 0;
    }
}

NotGate类:继承Component,包含一个输入引脚和一个输出引脚。输出引脚的电平与输入引脚的电平相反,如输入为低电平,输出则为高电平。

class NotGate extends Component {
    NotGate(String name) {
        super(name);
    }

    @Override
    int analysis(List<Integer> list) {
        return list.get(0) == 0 ? 1 : 0;
    }
}

XnorGate类:继承Component,包含两个输入引脚和一个输出引脚。当两个输入引脚电平不一致时输出引脚输出高电平,否则输出低电平。

class XnorGate extends Component {
    XnorGate(String name) {
        super(name);
    }

    @Override
    int analysis(List<Integer> list) {
        return 1 - (list.get(0) ^ list.get(1));
    }
}

XorGate类:继承Component,包含两个输入引脚和一个输出引脚。当两个输入引脚电平一致时输出引脚输出高电平,否则输出低电平。

class XorGate extends Component {
    XorGate(String name) {
        super(name);
    }

    @Override
    int analysis(List<Integer> list) {
        return list.get(0) ^ list.get(1);
    }
}

 

对代码进行分析,有:

屏幕截图 2026-06-24 145815

可以看到,最大圈复杂度为 11,已超过建议的警戒线(10),说明存在个别方法逻辑偏复杂,有潜在出 bug 的风险,建议优先重构。平均每个方法有 6.13 条语句,方法体短小精悍,符合单一职责原则,属于优秀范围。由于是在PTA中编写,所以在这里我只设立了一个大类Main。

本次我的程序的类图如下:

屏幕截图 2026-06-24 150321

第二次作业

目的:本次作业要求实现的新增四个元件,并能增加对元件的解析。

在此处我仅展示新增的四个类

TriGate类:继承Component,包含一个输入引脚、一个输入控制引脚、一个输出引脚。当控制引脚为高电平时,三态门输入输出之间导通,输出电平等于输入电平;当控制引脚为低电平时,三态门输入输出之间呈现高阻态(类似开关断开),输出为无效状态。

class TriGate extends Component {
    TriGate(String n){super(n);}
    @Override
    void calc(List<Integer> list){
        if(list.size()<2){
            outVal=null;
            return;
        }
        int ctrl=list.get(0);
        int dat=list.get(1);
        if(ctrl==1){
            outVal=dat;
        }else{
            outVal=null;
        }
    }
    @Override
    void printOut(){
        if(outVal==null)return;
        System.out.println(name+"-2:"+outVal);
    }
}

DecoderGate类:继承Component,译码器的作用是将输入的编码转换为一路有效信号。一个译码器包含两个或多个输入引脚(如图中的A2\A1\A0)、三个控制引脚(如图中的S3\S2\S1)、4个或多个输出引脚(如图中的Y7~Y0)。根据输入输出的数量有2-4线译码器、3-8线译码器等,当控制引脚当S1 =1,S2 +S3 =0时,译码器正常工作,输出引脚只有一个输出信号0,其余输出为1;哪个引脚输出0由输入引脚的编码决定。

class DecoderGate extends Component {
    int inCnt;
    DecoderGate(String n){
        super(n);
        int l= n.indexOf('(');
        int r= n.indexOf(')');
        inCnt=Integer.parseInt(n.substring(l+1,r));
    }
    @Override
    void calc(List<Integer> list){
        int s1=list.get(0);
        int s2=list.get(1);
        int s3=list.get(2);
        if(s1==1 && s2==0 && s3==0){
            int code=0;
            for(int i=3;i<list.size();i++){
                code=code*2+list.get(i);
            }
            outVal=code;
        }else{
            outVal=null;
        }
    }
    @Override
    void printOut(){
        if(outVal==null)return;
        System.out.println(name+"1:"+outVal);
    }
}

MuxGate类:继承Component,数据选择器的作用是从多路输入信号中选择一个,并将其信号直接送往唯一的输出端,选择哪一路输入信号由控制端决定。

class MuxGate extends Component {
    int ctrlNum;
    int dataNum;
    List<Integer> dataBuf;
    MuxGate(String n){
        super(n);
        int l=n.indexOf('(');
        int r=n.indexOf(')');
        ctrlNum=Integer.parseInt(n.substring(l+1,r));
        dataNum=(int)Math.pow(2,ctrlNum);
    }
    @Override
    void calc(List<Integer> list){
        if(list.size()<dataNum+ctrlNum){
            outVal=null;
            return;
        }
        dataBuf=new ArrayList<>();
        for(int i=0;i<dataNum;i++){
            dataBuf.add(list.get(i));
        }
        int idx=0;
        for(int i=dataNum;i<list.size();i++){
            idx=idx*2+list.get(i);
        }
        outVal=idx;
    }
    @Override
    void printOut(){
        if(outVal==null) return;
        String resStr="";
        for(int i=0;i<dataNum;i++){
            if(i==outVal)
                resStr=resStr+dataBuf.get(i);
            else
                resStr=resStr+"-";
        }
        System.out.println(name+resStr+"3:"+dataBuf.get(outVal));
    }
}

DemuxGate类:继承Component,数据分配器的作用与数据选择器正好相反,是将唯一的一路输入信号输出到多路输出引脚的其中之一,选择哪一路输出引脚输出由控制端决定。

class DemuxGate extends Component {
    int ctrlNum;
    int outNum;
    int selectPos;
    int dataVal;
    DemuxGate(String n){
        super(n);
        int l=n.indexOf('(');
        int r=n.indexOf(')');
        ctrlNum=Integer.parseInt(n.substring(l+1,r));
        outNum=(int)Math.pow(2,ctrlNum);
    }
    @Override
    void calc(List<Integer> list){
        if(list.size() < 1 + ctrlNum){
            outVal=null;
            return;
        }
        dataVal = list.get(0);
        int pos=0;
        for(int i=1;i<list.size();i++){
            pos = pos*2 + list.get(i);
        }
        selectPos = pos;
        outVal=pos;
    }
    @Override
    void printOut(){
        if(outVal==null) return;
        String resStr="";
        for(int i=0;i<outNum;i++){
            if(i == selectPos){
                resStr = resStr + dataVal;
            }else{
                resStr = resStr + "-";
            }
        }
        System.out.println(name+"1:"+resStr);
    }
}

对代码进行分析,有:

屏幕截图 2026-06-24 151608

可以看到,最大圈复杂度为 12,略高于建议的警戒线(10),说明存在个别方法逻辑偏复杂,存在一定的出 bug 风险,建议对这些高复杂度方法进行拆分优化。平均每个方法有 5.85 条语句,远低于常见阈值(20),方法粒度控制得非常精细,可读性和可维护性良好。

对应类图为:

屏幕截图 2026-06-24 151900

第三次作业

目的:新增子电路概念和异常输入处理。

SubCircuit类:

static class SubCircuit {
        String id;
        List<String> connLines = new ArrayList<>();
        Map<String, Integer> portVal = new HashMap<>();
        Map<String, Gate> gates = new HashMap<>();
    }

 

parseSubCircuit类:
static int parseSubCircuit(List<String> data, int start) {
        String head = data.get(start++);
        String sid = head.substring(0, head.length() - 1);
        SubCircuit sub = new SubCircuit();
        sub.id = sid;
        while (start < data.size()) {
            String line = data.get(start++).trim();
            if ("endc".equals(line)) break;
            if (line.startsWith("[")) {
                sub.connLines.add(line);
            }
        }
        subList.add(sub);
        return start;
    }

对代码进行分析,有:

屏幕截图 2026-06-24 152420

可以看到,最大圈复杂度达到 24,远高于建议的警戒线(10-20),说明代码中至少有一个方法逻辑非常复杂,存在较高的出 bug 风险,建议立即对该方法进行拆分重构。平均每个方法有 19.70 条语句,接近常见的 20 条警戒值,整体方法粒度偏大,建议进一步拆分以提升可读性和可维护性。

类图如下:

屏幕截图 2026-06-24 152507

 

对比三次作业的类图来看,一次比一次复杂,对于我的考验也一次比一次大,我深刻的意识到离开了已给出类图的我根本无法进行编程,这也提醒了我要不断进步,根据已给出的类图进行编程并不是自己的能力,而是当能自己设计类并实现才是自己有能力的体现。

踩坑心得

由于第一次作业时设计的类存在严重缺陷,所以在后续的作业里完成的举步维艰,所以说基础是最重要的,如果我第一次作业能花费更多的心思设计好类,后续的作业就能轻松一点,应该来讲,这是我做的6次作业中踩的最大的坑了。

 

改进建议

我认为第三次作业的代码实在是有点难看,从第三次作业我的代码的类图就可以看出来,我应该把第三次的代码修改一下,分出类来便于代码维护,同时也须知第一次作业的类设计 基础一定一定要设计好,否则接下来的迭代将会很痛苦。

 

 

总结

以后要认真对待每一次作业,不能因为第一次题给出的要求简单就不花心思,更相反,第一次作业往往是最重要的一次,这次踩的坑我将铭记于心,端正学习态度。

posted @ 2026-06-24 15:39  落枫yy  阅读(2)  评论(0)    收藏  举报