面向对象程序设计 题目集5-7总结BLOG

一.前言

通过这3次电梯调度的题目,我巩固并学习到了很多知识点:

1.正则表达式的正确使用
2.如何设计类以及单一职责的实现
3.类间关系的合理设计
4.ArrayList和枚举类型的使用

除此之外,通过这几次的作业,我更加熟悉并掌握了java的基本语法以及封装性,对面向对象的设计思路有了进一步的认识。这次的作业相比之前难度提升了很多,以至于在第一周的时候无从下手。通过老师的提醒,我认识到了读懂题目和要求的重要性。一开始的想法是错的,通过对老师发的文件的专研以及和同学们一起讨论,我才逐步了解到了题目的意思,也有了写代码的思路。但是由于读懂及开始写的时间太晚 导致第一次题目集没有完成。在隔天通过了第一次的测试点,第二次划分类后就比较快的完成了。但是第三次作业还存在部分测试点未过,可能是代码逻辑还不完善。

二.设计与分析

1.第五周电梯题目

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

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

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

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

所有方法及作用

Direction 枚举
这是一个简单的枚举类型,定义了电梯的运行方向,包含三个值:UP(向上)、DOWN(向下)和 IDLE(空闲)。它没有具体的方法。
ElevatorSystem 类
静态内部类 ExternalRequest
ExternalRequest(int floor, Direction direction)
作用:构造函数,用于创建一个外部请求对象,包含请求的楼层和方向信息。
参数:
floor:请求的楼层。
direction:请求的方向(UP 或 DOWN)。
构造函数
ElevatorSystem(int minFloor, int maxFloor)
作用:初始化电梯系统的基本信息,包括最小楼层、最大楼层、当前楼层和当前运行方向。
参数:
minFloor:电梯运行的最小楼层。
maxFloor:电梯运行的最大楼层。
公共方法
addInternalRequest(int floor)
作用:将一个内部请求添加到内部请求队列中。在添加之前,会检查楼层是否在有效范围内。
参数:
floor:请求的楼层。
addExternalRequest(int floor, Direction direction)
作用:将一个外部请求添加到外部请求队列中。在添加之前,会检查楼层是否在有效范围内。
参数:
floor:请求的楼层。
direction:请求的方向(UP 或 DOWN)。
getInternalRequests()
作用:返回内部请求队列。
返回值:Queue 类型的内部请求队列。
getExternalRequests()
作用:返回外部请求队列。
返回值:Queue 类型的外部请求队列。
processRequests()
作用:处理所有的内部和外部请求,直到两个队列都为空。通过不断调用 moveElevator() 方法来移动电梯。
moveElevator()
作用:实现电梯的移动逻辑。根据内部和外部请求队列的情况,确定目标楼层和运行方向,然后移动电梯到目标楼层。在移动过程中,会检查是否需要开门,并处理停止和改变方向的情况。
私有方法
isValidFloor(int floor)
作用:验证给定的楼层是否在电梯运行的有效范围内(即大于等于最小楼层且小于等于最大楼层)。
参数:
floor:要验证的楼层。
返回值:如果楼层有效,返回 true;否则返回 false。
handleStop()
作用:处理电梯在当前楼层停止的情况,打印开门和关门的信息。
removeRequestsAtTargetFloor(int currentfloor)
作用:从内部和外部请求队列中移除当前楼层的请求。如果内部请求队列的头部请求楼层等于当前楼层,则移除该请求;如果外部请求队列的头部请求楼层等于当前楼层,则移除该请求。
参数:
currentfloor:当前楼层。
shouldopen()
作用:判断电梯是否应该在当前楼层开门。检查内部和外部请求队列的头部请求,如果当前楼层等于请求楼层且方向匹配,则返回 true。
返回值:如果应该开门,返回 true;否则返回 false。
shouldChangeDirection()
作用:判断电梯是否应该改变运行方向。根据内部和外部请求队列的头部请求楼层与当前楼层的关系,以及当前运行方向,决定是否需要改变方向。
返回值:如果应该改变方向,返回 true;否则返回 false。
Main 类
main(String[] args)
作用:程序的入口点。从用户输入中读取电梯的最小楼层、最大楼层和乘客请求,创建 ElevatorSystem 对象,并将请求添加到相应的队列中。最后调用 processRequests() 方法处理所有请求。

*代码分析

*思考

起初,由于没有沉下心认真研读作业需求,整个学习状态陷入浑浑噩噩的困境。面对代码框架和功能要求,大脑一片空白,完全找不到着手点,到了后面才知道大概的模式导致时间太紧,没有完成作业一开始是没有明白方向优先究竟是什么意思,到了后面看老师发的输出输入样例才懂得一些。但此时时间已十分紧迫,最终导致作业未能按时完成。
电梯运行的整体逻辑一开始是理解错了,和同学们讨论了之后才逐渐懂得了正确的移动逻辑,正确明白了方向优先到底是什么意思

*收获

这次题目集我明白了需求的重要性,想写代码就必须先看懂需求,不能漫无目的的写代码。在思考需求时更需要我们静
下心来思考,不能太浮躁,一味想要写出代码而无厘头的写,毫无意义。将大量重复性的代码可以单独拿出来,少使用
if-else语句

2.第六周电梯题目

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,具体设计可参考如下类图。
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>

*参考类图

*代码分析

所有方法及作用

main 方法(位于 main 类中):
功能:程序的入口点,从控制台读取输入数据,处理输入并创建相关对象,调用 Controller 的方法来处理电梯请求。
Direction 枚举(位于文件中独立定义):
成员:UP、DOWN、IDLE,表示电梯的运行方向。
ExternalRequest 类:
ExternalRequest(int floor, Direction direction):构造函数,用于创建外部请求对象,设置楼层和方向。
getFloor():获取请求的楼层。
setFloor(int floor):设置请求的楼层。
getDirection():获取请求的方向。
setDirection(Direction direction):设置请求的方向。
Elevator 类:
Elevator(int maxFloor, int minFloor):构造函数,初始化电梯的当前楼层、方向、最高楼层和最低楼层。
getCurrentFloor():获取电梯当前所在楼层。
setCurrentFloor(int currentFloor):设置电梯当前所在楼层。
getDirection():获取电梯当前运行方向。
setDirection(Direction currentDirection):设置电梯运行方向。
getMaxFloor():获取电梯可到达的最高楼层。
getMinFloor():获取电梯可到达的最低楼层。
isValidFloor(int floor):判断指定楼层是否在电梯可运行范围内。
Requestqueue 类:
Requestqueue():无参构造函数,初始化内部请求队列和外部请求队列。
getqueueInstance():获取请求队列实例(实际返回当前实例)。
getInternalRequests():获取内部请求队列。
setInternalRequests(LinkedList internalRequests):设置内部请求队列。
getExternalRequests():获取外部请求队列。
setExternalRequests(LinkedList externalRequests):设置外部请求队列。
addInternalRequest(int floor):向内部请求队列添加一个内部请求。
addExternalRequest(int floor, Direction direction):向外部请求队列添加一个外部请求。
Controller 类:
Controller(int maxFloor, int minFloor):构造函数,初始化电梯和请求队列对象。
Controller(Elevator elevator, Requestqueue queue):构造函数,通过传入电梯和请求队列对象进行初始化。
getElevator():获取电梯对象。
setElevator(Elevator elevator):设置电梯对象。
getQueue():获取请求队列对象。
setQueue(Requestqueue queue):设置请求队列对象。
addInternalRequest(int floor):添加内部请求到请求队列,若楼层有效。
addExternalRequest(int floor, Direction direction):添加外部请求到请求队列,若楼层有效。
processRequests():处理所有的内部和外部请求,直到请求队列为空。
moveElevator():移动电梯,根据请求队列情况处理电梯的运行方向和楼层变化。
handleStop():处理电梯停止的情况,打印开门和关门信息。
removeRequestsAtTargetFloor(int currentfloor):移除目标楼层的内部和外部请求。
shouldopen():判断电梯是否应该在当前楼层开门,检查内部和外部请求队列。
shouldChangeDirection():判断电梯是否应该改变运行方向,根据内部和外部请求队列的情况进行判断。

*思考

此次代码就是在上一次的基础上实现类的单一职责,由于对原代码的逻辑已经有了较为深入的理解,所以在将其按照单一职责原则拆分成多个类的过程中,并没有花费太多的时间。分析了原代码中各个功能模块的职责,将不同的功能分别封装到不同的类中,使得每个类只负责一项特定的任务。一开始出现了答案错误的情况,发现这是由于在处理特定情况时,多执行了一次开门信息的打印语句,将那个多余的打印语句删掉,问题就得到了解决。

*收获

我明白了如何通过合理地划分类的职责,将一个复杂的功能拆分成多个简单的、独立的类,从而使代码更加易于理解和维护。更加清楚了单一职责是如何实现的,并且对类间的调用有了更深刻的认识,同时更加了解了调用的逻辑与规则和java面向对象的思想

3.第七周电梯题目

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

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

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

以下是提前终止了输出的删除代码

private void removeRequestsAtTargetFloor(int currentfloor) {
        Queue<Passenger> internalRequests = queue.getInternalRequests();
        // 删除内部请求队列头部的请求
        if (!internalRequests.isEmpty()) {
            Passenger internalPassenger = internalRequests.peek();
            int targetFloor = internalPassenger.getDestinationFloor();
            if(!internalRequests.isEmpty() && targetFloor == elevator.getCurrentFloor()) {
                internalRequests.poll();
            }
        }

        Queue<Passenger> externalRequests = queue.getExternalRequests();
        // 删除外部请求队列头部的请求
        if (!externalRequests.isEmpty()) {
            Passenger externalPassenger = externalRequests.peek();
            int sourceFloor = externalPassenger.getSourceFloor();
            while (!externalRequests.isEmpty() && sourceFloor == elevator.getCurrentFloor()) {
                // 获取请求目的楼层
                int destinationFloor = externalPassenger.getDestinationFloor();
                // 创建新的内部请求乘客对象
                Passenger newInternalPassenger = new Passenger(null, destinationFloor);
                // 将新的内部请求加入内部请求队列队尾
                internalRequests.add(newInternalPassenger);
                // 移除当前外部请求
                externalRequests.poll();

            }
        }

    }

以下是修改后的代码

private void removeRequestsAtTargetFloor(int currentFloor) {
        Queue<Passenger> internalRequests = queue.getInternalRequests();
        if (!internalRequests.isEmpty()) {
            Passenger internalPassenger = internalRequests.peek();
            int targetFloor = internalPassenger.getDestinationFloor();
            if (!internalRequests.isEmpty() && targetFloor == elevator.getCurrentFloor()) {
                internalRequests.poll();
            }
        }

        Queue<Passenger> externalRequests = queue.getExternalRequests();
        if (!externalRequests.isEmpty()) {
            Passenger externalPassenger = externalRequests.peek();
            int sourceFloor = externalPassenger.getSourceFloor();
            if (!externalRequests.isEmpty() && sourceFloor == elevator.getCurrentFloor()) {
                int destinationFloor = externalPassenger.getDestinationFloor();
                Passenger newInternalPassenger = new Passenger(null, destinationFloor);
                internalRequests.add(newInternalPassenger);
                externalRequests.poll();
            }
        }
    }

所有方法及作用

main 类
main(String[] args):程序的入口方法,负责读取用户输入、创建 Controller 对象、添加内部和外部请求,最后调用 processRequests 方法处理请求。
Direction 枚举
无方法,仅包含三个枚举常量:UP、DOWN、IDLE。
Elevator 类
Elevator(int maxFloor, int minFloor):构造方法,初始化电梯的当前楼层、运行方向、最高楼层和最低楼层。
getCurrentFloor():获取电梯当前所在楼层。
setCurrentFloor(int currentFloor):设置电梯当前所在楼层。
getDirection():获取电梯当前的运行方向。
setDirection(Direction direction):设置电梯的运行方向。
getMaxFloor():获取电梯可到达的最高楼层。
getMinFloor():获取电梯可到达的最低楼层。
isValidFloor(int floor):判断指定楼层是否在电梯可运行的楼层范围内。
Passenger 类
Passenger():无参构造方法,将源楼层和目的楼层初始化为 null。
Passenger(Integer sourceFloor, Integer destinationFloor):有参构造方法,初始化乘客的源楼层和目的楼层。
Passenger(Integer destinationFloor):有参构造方法,只初始化乘客的目的楼层,源楼层为 null。
getSourceFloor():获取乘客的源楼层。
setSourceFloor(Integer sourceFloor):设置乘客的源楼层。
getDestinationFloor():获取乘客的目的楼层。
setDestinationFloor(Integer destinationFloor):设置乘客的目的楼层。
getDirection():根据源楼层和目的楼层判断乘客的移动方向。
RequestQueue 类
RequestQueue():无参构造方法,初始化内部请求队列和外部请求队列。
getQueueInstance():返回当前 RequestQueue 对象实例。
getInternalRequests():获取内部请求队列。
setInternalRequests(LinkedList internalRequests):设置内部请求队列。
getExternalRequests():获取外部请求队列。
setExternalRequests(LinkedList externalRequests):设置外部请求队列。
addInternalRequest(Passenger passenger):向内部请求队列中添加一个乘客请求。
addExternalRequest(Passenger passenger):向外部请求队列中添加一个乘客请求。
Controller 类
Controller(int maxFloor, int minFloor):构造方法,创建 Elevator 和 RequestQueue 对象。
Controller(Elevator elevator, RequestQueue queue):构造方法,使用传入的 Elevator 和 RequestQueue 对象进行初始化。
getElevator():获取 Elevator 对象。
setElevator(Elevator elevator):设置 Elevator 对象。
getQueue():获取 RequestQueue 对象。
setQueue(RequestQueue queue):设置 RequestQueue 对象。
addInternalRequest(Passenger passenger):如果目的楼层有效,将乘客的内部请求添加到请求队列中。
addExternalRequest(Passenger passenger):如果源楼层和目的楼层都有效,将乘客的外部请求添加到请求队列中。
processRequests():处理所有的内部和外部请求,直到队列都为空。
moveElevator():移动电梯,根据请求队列情况决定电梯的运行方向和楼层变化。
handleStop():处理电梯在当前楼层的停止操作,打印开门和关门信息。
removeRequestsAtTargetFloor(int currentFloor):移除当前楼层的内部和外部请求,若有外部请求,将其转换为内部请求。
shouldOpen():判断电梯是否应该在当前楼层开门。
shouldChangeDirection():判断电梯是否应该改变运行方向。

*参考类图

*代码分析

*思考

这次的迭代改了输入,多了一个乘客类,去掉了外部请求类。然而,在测试过程中,遇到了一系列问题。首先,电梯出现提前停止的情况。经过仔细排查,发现是代码中的 if 语句导致了提前终止输出。这表明在逻辑判断上存在不够严谨的地方,使得电梯在不应该停止的时候停止了运行。
接着,我发现一些特殊案例无法实现方向优先的功能。虽然我已经定位到删除逻辑不够清晰可能是问题所在,但在尝试改进的过程中,又出现了新的问题 —— 运行超时。推测可能是某些循环或者递归操作没有正确终止,或者是算法复杂度太高,导致程序在处理特殊案例时耗费了过多的时间。目前,我还没有找到很好的改进方法,这使得部分测试点无法通过。

*收获

尽管这次迭代遇到了不少困难,没有完成作业,但我也从中获得了宝贵的经验。在处理方向优先问题时,我意识到自己考虑的情况还不够全面,需要更加深入地思考各种边界条件和特殊情况。这也让我明白,在编写代码之前,充分的设计和规划是多么重要。

三.采坑心得

有重复并且很长的代码可以想办法另外单独写一个方法,在需要的时候调度。太多的if-else语句会导致运行超时等错误,我们可以使用别的
方法如while等方法进行改善,代码会更简短精炼更高效。在第一次题目集到每个楼层进行判断的时候就因为太多重复的语句且用if分情况导
致运行超时,后用while循环语句代替就解决了这一问题。
这次采坑经历让我明白,编写代码不能只满足于实现功能,更要注重代码的质量和性能。合理运用方法封装和选择合适的控制流程语句,能够让代码更加简短精炼、高效可靠。

原代码:

while (currentDirection == Direction.UP) {
    currentFloor++;
    System.out.println("Current Floor: " + currentFloor + " Direction: " + currentDirection);
    if (shouldChangeDirection()) {
        currentDirection = currentDirection == Direction.UP? Direction.DOWN : Direction.UP;
     removeRequestsAtTargetFloor(currentFloor);
    }
     if (shouldopen()) {
       
        handleStop();
    }
}

while (currentDirection == Direction.DOWN) {
    currentFloor--;
    System.out.println("Current Floor: " + currentFloor + " Direction: " + currentDirection);
    if (shouldChangeDirection()) {
        currentDirection = currentDirection == Direction.UP? Direction.DOWN : Direction.UP;
        removeRequestsAtTargetFloor(currentFloor);
    }
    if (shouldopen()) {
        handleStop();
    }
    
}

修改后的代码:

while (!internalRequests.isEmpty() &&!externalRequests.isEmpty()) {
            if (currentDirection == Direction.UP) {
                currentFloor++;
            } else {
                currentFloor--;
            }
            System.out.println("Current Floor: " + currentFloor + " Direction: " + currentDirection);

            if (shouldopen()) {
                handleStop();
                removeRequestsAtTargetFloor(currentFloor);
            }

            if (shouldChangeDirection()) {
                currentDirection = currentDirection == Direction.UP? Direction.DOWN : Direction.UP;
            }
        }

四.改进建议

1.moveElevator 方法太长,可以拆分成处理外部请求、内部请求、方向判断等小方法。
2.可以优化逻辑,使逻辑更加清楚正确
3.何时需要改变方向的代码还需要进一步优化
4.处理到达目标楼层后移除请求的代码,内部和外部请求处理有重复,可以提取公共方法,
合并内部和外部请求的目标楼层处理逻辑
5.可以添加更多的注释,具体说明代码的功能和用途

五.总结

通过这3次的题目集,我学会了正则表达式的正确使用,设计类以及单一职责的实现,类间关系的合理设计以及ArrayList和枚举
类型的使用。通过这次题目集让我意识到了解需求的重要性,拿到题目首先要分析清楚需求,再来画好流程图设计类图,想好类
间的关系,最后再来写代码。而不是一拿到题目还没搞清楚要求就开始写代码,这样不仅浪费时间而且是没有正确清楚的方向,
效率很低,要静下心来思考,有自己的思维。

对于我自己来说,还需要提升自己的逻辑思维,设计类间关系的能力,Arraylist的使用方法还需要进一步熟练。代码的可读性也
要进一步提升,可以通过增加注释来提高可读性。java的基本语法和一些细节问题也要多加注意,通过多加练习来改善细节问题。

总得来说,这次的电梯题目集每次都在上一次的基础上迭代更新,虽然说对我们有很大的挑战性,也有不小的难度。但是通过这次
题目集可以很好的提升我们的逻辑思维,看待问题的能力以及编程的能力,使我们对类间关系有了更好的理解,更好的掌握新学的知识点。

线下课我觉得老师可以讲得更清楚详细一点,有难度的地方可以更慢一些,顾及一下学生们的能力。

posted @ 2025-04-18 17:29  月跃悦  阅读(50)  评论(0)    收藏  举报