NCHU 面向对象程序设计 BLOG1

NCHU 面向对象程序设计 Blog 1
前言
关于面向对象的概念:
关于这几次练习:
课程内容总结
PTA第五周题目集
题目
思考:
参考类图:
代码实现:
代码分析:
收获
第六周题目集
题目
思考:
参考类图:
代码实现:
代码分析:
收获
第七周题目集
题目
思考:
参考类图:
代码实现:
代码分析:
收获
5~7周pta题目经验总结
对自己想说的话
关于课程和作业

前言

关于面向对象的概念:

从面向过程的编程到面向对象的编程思维转变。 我回顾了开学前两周做的课设, 完全的面向过程 ,很冗杂,各种关系嵌套,这就是所谓的“屎山代码”为了完成任务而创建的代码.
经过这几周的pta练习我了解并运用了一些重要的面向对象程序的重要概念如​​单一职责原则 (Single Responsibility Principle, SRP),​​开闭原则 (Open/Closed Principle, OCP)等等.

关于这几次练习:

前面的几道小题都算比较简单的,都是对一些java的一些重要的类的运用和练习。
通过这几次练习我学到了一下知识点:

1. 类和对象的创建和使用

2. 类间关系的实现

3. 基础的程序设计原则

4. 正则表达式的灵活运用

6. LocalDate,Hashset,ArrayList等内部类的使用等
这次大作业的焦点必然是我们的电梯调度程序啦(第一周快截至的时候大伙都没写出来)
后面两次迭代都是在此基础上近些多类迭代以及一些创新。我个人在第六周和第七周完成的时间远不及第五周思考电梯的时间(但是还是有点小坑在里面的)。

课程内容总结

类间关系有 关联 ,依赖,聚合(包括组合和聚合),继承(泛化);
ArrayList及collection集合里set map 的区别;
String类的方法.
单一职责原则 以及职责的分配。

PTA第五周题目集

题目:

设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:

运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door

思考:

第五周刚刚接触这种大作业 首先我的关注点在于是否会在移动中处理顺路的请求,
所以我开始回想平时自己搭乘电梯是怎么样的, 同时题目说了不会在中途加入更多的请求,以电梯的视角,在某一时刻收到的请求,假设此时电梯上行,电梯必然会去最高的上行楼层同时把 外部上行请求的乘客接上 在运行的过程中
每到一个楼层检测是否有内部请求和相同方向的外部请求满足条件并开门(至少我在平时日常生活中见到的电梯是这样运行的)导致我一开始设计的也是这样 样例也能通过但是答案错误 不理解 后面老师在群里发了是先来先服务原则
这与之前提到的clook算法有较大差异 所以我更改了我的想法 。

参考类图:

由于是第一次写这种大作业 而且也是刚刚起步 就只有一个elvator类就没有特别设计

代码实现:

首先经过正则表达式筛选后 会进入到电梯的关键循环部分

 public void run() {
        while (hasrequests()) {
            int targetFloor = gettargetFloor();

            move (targetFloor);
        }
    }

然后进入关键的调度程序

public int gettargetFloor() {
        int result = 0;
        Direction tempinnerdirection = Direction.IDLE;
        Direction tempouterdirection = Direction.IDLE;
        if (internalRequests.isEmpty()) {
            int temp = Integer.parseInt(externalRequests.get(0).split(",")[0]);
            return temp;

        } else if (externalRequests.isEmpty()) {
            return internalRequests.get(0);
        }
        tempinnerdirection = internalRequests.get(0) > currentFloor ? Direction.UP : Direction.DOWN;
        if (externalRequests.get(0).split(",")[1].equals("UP")) {
            tempouterdirection = Direction.UP;
        } else if (externalRequests.get(0).split(",")[1].equals("DOWN")) {
            tempouterdirection = Direction.DOWN;
        }
//处理方向优先;
        if (direction == tempinnerdirection && direction == tempouterdirection || direction != tempinnerdirection && direction != tempouterdirection) {
            result = closepriotity(internalRequests.get(0), Integer.parseInt(externalRequests.get(0).split(",")[0]));
        } else if (direction == tempinnerdirection) {
            result = internalRequests.get(0);
        } else if (direction == tempouterdirection) {
            result = Integer.parseInt(externalRequests.get(0).split(",")[0]);
        }
        return result;
    }

代码分析:

对本次代码用Source Monitor进行分析:

190行代码​​:属于中小型文件,但需结合功能复杂度评估是否合理。
​​120条语句​​:语句/行数比≈0.63,说明代码密度较高(可能存在单行多语句或缺少格式化)。
5.8%的注释行​​:显著低于业界推荐的15-20%标准,可能影响可维护性。建议对复杂逻辑(如Elevator.move())补充文档注释。
位于144行,结合​​分支语句占比24.2%​​,推测存在多层条件/循环逻辑。

收获

有时候题目是题目 现实中不一定对应地上 应该更加仔细地阅读完题目在进行思考 否则只是在错误地路上越走越远
通过代码分析也可以看出此次我的代码地注释率不高 可读性也不强 同时圈复杂度也很高 在调度算法中分支语句太多
类的设计不够合理 elevator干了太多事情 不符合单一职责原则

第六周题目集

题目

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,具体设计可参考如下类图。
电梯运行规则与前阶段单类设计相同,但要处理如下情况:

乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
注意:本次作业类设计必须符合如上要求(包含但不限于乘客请求类、电梯类、请求队列类及控制类,其中控制类专门负责电梯调度过程),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次电梯程序提交到本次题目中测试)。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:

运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door

思考

这次迭代主要是增加了多类设计以及过滤请求 然后对于样例也进行了更高的要求,因为原有的elevator类包含了太多调用关系,同时我看不懂(上周写的代码基本没啥注释)我干脆就
重写写了代码 这次这个过滤类一开始我以为是只能出现同一个请求 所以一开始我想到了用hash表存储因为键是唯一的 后来发现原来只要不连续就行了 就废弃了就运用了更为简单的if语句判断
这次的样例同样对电梯停靠做出了新的要求 需要应对更多不同情况的状态。

参考类图

代码实现

下面这一段是实现过滤的效果

 public ArrayList<Integer> filterInternalRequests(ArrayList<Integer> requests) {
        ArrayList<Integer> result = new ArrayList<>();
        Integer prevFloor = null; // 用于跟踪前一个处理的楼层
        for (int floor : requests) {
            if (!isValidFloor(floor)) continue; // 检查楼层是否有效
            // 只保留不连续重复的请求
            if (prevFloor == null || floor != prevFloor) {
                result.add(floor);
                prevFloor = floor; // 更新前一个有效楼层
            }
        }
        return result;
    }

以下是新增的对于更复杂情况的判断

 if (elevator.getCurrentFloor() == elevator.getMaxFloor()) {
            elevator.setDirection(Direction.DOWN);
        }
        if (elevator.getCurrentFloor() == elevator.getMinFloor()) {
            elevator.setDirection(Direction.UP);
        }
        if (!(requestQueue.getExternalRequests().isEmpty() || requestQueue.getInternalRequests().isEmpty())) {
            if (elevator.getCurrentFloor() > requestQueue.getExternalRequests().get(0).getFloor() && elevator.getCurrentFloor() > requestQueue.getInternalRequests().get(0)) {
                elevator.setDirection(Direction.DOWN);
            } else if (elevator.getCurrentFloor() < requestQueue.getExternalRequests().get(0).getFloor() && elevator.getCurrentFloor() < requestQueue.getInternalRequests().get(0)) {
                elevator.setDirection(Direction.UP);
            }
        }

代码分析

Lines:代码行数为 429 行,代码规模适中。
Statements:语句数 179 条,反映代码中执行操作的数量。
Percent Branch Statements:分支语句占比 24.0% ,说明代码中条件判断(如 if、switch 等)相关代码占比较明显,代码逻辑有一定复杂性。
Method Call Statements:方法调用语句 140 条,表明代码中频繁调用方法,体现了模块化编程的特点。
Percent Lines with Comments:注释行占比 19.6% ,说明代码有一定注释量,但不算特别高,代码可读性有提升空间。

收获

本次迭代只是进行了多类的设计原本只是一件很简单的事 增加的功能也算是 拍一拍脑子就能实现 但是由于我一个小小的失误导致耗费了我至少三个小时以上
不说了直接上图都是泪


我由于没有大写'F'一直以为是自己哪里没考虑到 原来是重构的时候 用了代码补全 自动补全的floor 而非 Floor 以后要慎用代码补全

第七周题目集

题目

对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。

电梯运行规则与前阶段相同,但有如下变动情况:

乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)

思考

此次的迭代相对来说实现起来很简单 但是由于在原来的类上去除了一个高耦合的类 导致各个类之间都要进行修改
filter类要进行passenger类泛型的修改总体来说这个类的复用性很高只要修改相关泛型就行了
以及这次新增了开门方向的判定 实现也不算难 总体来说我没有花太多时间

参考类图

代码实现

filter类的修改

public ArrayList<Passenger> filterInternalRequests(ArrayList<Passenger> requests) {
        ArrayList<Passenger> result = new ArrayList<>();
        Passenger prevRequest = null; // 跟踪前一个处理请求

        for (Passenger req : requests) {
            // 修改点2:检查目标楼层有效性
            if (!isValidFloor(req.getSourceFloor())) continue;

            // 修改点3:去重逻辑基于目标楼层
            if (prevRequest == null ||
                    req.getSourceFloor() != prevRequest.getSourceFloor()) {
                result.add(req);
                prevRequest = req;
            }
        }
        return result;
    }
同方向开门实现
```plaintext
 if (!requestQueue.getExternalRequests().isEmpty()) {

            if (requestQueue.getExternalRequests().get(0).getSourceFloor() == elevator.getCurrentFloor()&&elevator.getDirection()==requestQueue.getExternalRequests().get(0).Getdirection()) {
                elevator.setDirection(requestQueue.getExternalRequests().get(0).Getdirection());
                requestQueue.addInternalRequest(requestQueue.getExternalRequests().get(0).getDestinationFloor());
                requestQueue.getExternalRequests().remove(0);
            }
        }
    }

代码分析


Lines:代码行数 537 行,代码规模相对适中。
Statements:语句数 186 条,体现代码操作数量。
Percent Branch Statements:分支语句占比 24.2% ,意味着条件判断逻辑占比较显著,代码逻辑有一定复杂度。
Method Call Statements:方法调用语句 150 条,表明代码模块化程度尚可,频繁调用方法实现功能。
Percent Lines with Comments:注释行占比 20.7% ,有一定注释量,但仍有提升空间以增强可读性。
Methods per Class:每个类平均方法数 8.50 ,类功能较丰富。
Average Statements per Method:每个方法平均语句数 10.76 ,方法内逻辑不算复杂。

收获

这次的快速完成很大程度上是上周的注释发力了而且通过本次作业我也认识到设计一个好的类是有多么难
实现起来简单 但是后面进行维护的成本会很高 代码复用性不强的话每周都会在重写的路上
前人栽树后人乘凉 一定要在写代码前仔细想想怎么设计才是最合理的 才会让本人(别人)一目了然

5~7周pta题目经验总结

对自己想说的话

经过前三周的oop课程学习和PTA的作业,切实感受到了这门课程的难度和课业压力;
虽然自己有在网上云学过类似的面向对象c#但是终归是云学习没有题目进行锻炼不知道自己是哪个小卡拉米
对自己要求太松了 每次都把java留在周末来做 虽然这有一个好处是 我可以连续地写代码 我的思路不会断 但是
如果遇到题目难的看不懂的情况 周末来写是真的来不及而且毁心情 恶性循环
还有永远不要孤军奋战

关于课程和作业

这次题目的测试点给的有点少 没有思考全电梯的各种情况 有些时候连签到分都拿不到 不过这样做也利于学生自己设计样例
关于课程,我觉得老师着重于程序设计的严格教学确实非常有意义,是切实对我们的设计和编程等的能力有很大帮助的;

posted @ 2025-04-17 11:31  无敌地平线大王  阅读(104)  评论(0)    收藏  举报