题目集5~7的总结
前言
(总结三次题目集的知识点、题量、难度等情况)
第五次题目集
本次题目集的题目分别为:
求身份证号校验位 、求解一元二次方程 、正则表达式训练-验证码校验 、正则表达式训练-QQ号校验 、单部电梯调度程序;
1. 知识点
主要知识点有(1).字符串处理(2).数组与循环(3).正则表达式(4).字符串转换为字符数组
2.题量
题量适中
3.难度
难度中等
第六次题目集
本次题目集的题目分别为:
点与线(类设计) 、汽车风挡玻璃雨刷问题(类设计) 、单部电梯调度程序(类设计)
1. 知识点
主要知识点有(1).字符串处理(2).数组与循环(3).正则表达式(4).字符串转换为字符数组(5).类设计
2.题量
题量较少
3.难度
难度中等
第七次题目集
本次题目集的题目分别为:
销售步枪问题 、 蒙特卡罗方法求圆周率 、单部电梯调度程序(类设计-迭代)
1. 知识点
主要知识点有(1).字符串处理(2).数组与循环(3).正则表达式(4).字符串转换为字符数组(5).类设计
2.题量
题量较少
3.难度
难度较难
设计与分析
(重点对题目的提交源码进行分析) (只分析三次题目集中的电梯题目)
第五次题目集
题目:单部电梯调度程序
指标项------数值------说明
类数量------2------Elevator + Main
方法总数------10------Elevator 类包含 8 个方法,Main 类 1 个方法
代码行数------~200------实际有效代码行数(不含空行和注释)
平均圈复杂度------4.2------较高复杂度方法:findNextFloor()(6)、determineDirection()(5)
注释比例------15% 方法级注释较完整,但部分逻辑块缺少说明
耦合度------低------仅依赖 java.util 和自定义 enum
关键结论:
核心逻辑集中在 Elevator 类,但 findNextFloor() 和 determineDirection() 方法复杂度较高,需考虑拆分。
类职责说明
Direction 枚举:定义电梯运行状态(上行、下行、空闲)。
OuterRequest 内部类:封装外部楼层请求(目标楼层+方向)。
Elevator 核心类:
属性:楼层范围、当前状态、内外请求队列。
方法:请求处理(addRequest)、调度逻辑(processRequests)、移动控制(moveToFloor)。
Main 类:处理输入输出,驱动电梯运行。
相应心得:
一、面向对象设计(OOP)的实践价值
封装与职责分离
将电梯状态(Direction)、请求(OuterRequest)等概念独立为类/枚举,避免全局变量污染。
Elevator 类内聚所有调度逻辑,对外仅暴露 addRequest() 和 processRequests() 方法,符合“高内聚低耦合”原则。
启示:OOP 能显著提升复杂逻辑的可维护性,尤其在状态机类场景(如电梯、订单流程)。
组合优于继承
使用 LinkedList 组合管理请求队列,而非继承 List 实现,保持灵活性。
反例:若强行用继承实现 PriorityQueue,会导致过度设计。
二、算法选择与性能权衡
SCAN 算法的适用性
当前实现类似电梯扫描算法(双向扫描),适合均匀分布的请求。
局限性:未考虑高峰期的请求密度差异(如早高峰大量上行请求)。
改进方向:可引入动态权重(如等待时间优先)或分区调度(如高层/低层分电梯)。
时间复杂度优化
findNextFloor() 的递归调用可能引发栈溢出(极端情况下)。
解决:改为循环结构,或预计算请求的最远目标楼层。
第六次题目集
指标------数值------健康度分析
类数量------5------职责分离良好(Controller/Queue/Elevator各司其职)
方法平均复杂度------2.1------最佳实践范围(1-3),仅shouldStop()方法复杂度为4需关注
代码行数------215------有效代码行数(不含空行/注释)
注释比例------18%------类/方法级注释完整,但部分复杂逻辑需补充行内注释
耦合度------0.3------低耦合设计(类间仅通过接口交互,无双向依赖)
相应心得:设计模式的价值
当前使用组合模式(Controller聚合子组件)比原版的单一类更易扩展
潜在改进:可引入观察者模式实现请求事件通知
测试驱动开发(TDD)的必要性
发现原版未处理"5,UP"和"5,DOWN"同时按下的冲突场景
测试用例建议:
@Test
void shouldPrioritizeSameDirectionRequests() {
controller.addRequest("3,UP");
controller.addRequest("5,DOWN"); // 应不被处理
assertEquals(3, elevator.getNextFloor());
}
性能与可读性的平衡
RequestQueue使用LinkedList适合低频请求,高频场景建议:
按楼层分桶:Map<Integer, Queue
使用ConcurrentLinkedQueue支持多线程
架构演进方向
mermaid
graph LR
A[单电梯] --> B[多电梯协同]
B --> C[分布式调度]
C --> D[容错与降级]
类职责说明:
一、模型层(Model)
- PassengerRequest 乘客请求类
核心职责:封装电梯请求的元数据
记录请求楼层(floor)
记录请求方向(direction,外部请求特有)
标识请求类型(isInternal,区分电梯内/外按钮)
关键方法:
equals():实现基于楼层+方向+类型的去重逻辑
设计思考:
使用不可变对象(字段final)保证线程安全
将方向与楼层绑定,符合物理世界逻辑("5楼向上按钮"是一个完整请求)
- RequestQueue 请求队列类
核心职责:管理请求的生命周期
存储待处理请求(Queue
过滤无效/重复请求(通过lastRequest比对)
提供请求消费接口(getNextRequest())
关键设计:
输入标准化:addRequest()方法统一处理<5>和<5,UP>两种格式
隐式验证:自动丢弃越界楼层(floor < minFloor时静默过滤)
改进建议:
可扩展为优先级队列(如按等待时间排序)
二、控制层(Controller)
- ElevatorController 电梯控制器
核心职责:协调电梯与请求队列的交互
接收原始输入并转发给RequestQueue(addRequest())
驱动电梯运行流程(run()方法的主循环)
工作流程:
mermaid
sequenceDiagram
Controller->>Queue: getNextRequest()
Queue-->>Controller: PassengerRequest
Controller->>Elevator: moveTo(request.floor)
Elevator->>Controller: 到达事件
Controller->>Elevator: openDoor()/closeDoor()
现存问题:
单线程顺序处理,无法实时响应新请求
- Direction 枚举
核心职责:定义电梯运行状态
UP/DOWN:明确双向运行逻辑(相比原版去除IDLE更简洁)
设计亮点:
避免魔法字符串,增强类型安全(如Direction.valueOf("UP"))
三、设备层(Device)
Elevator 电梯实体类
核心职责:模拟物理电梯行为
移动控制(moveTo()逐层更新状态)
门控制(openDoor()/closeDoor())
停止判断(shouldStop()结合内外请求逻辑)
关键算法:
java
// 停止条件判断逻辑
public boolean shouldStop(PassengerRequest request) {
return request.getFloor() == currentFloor &&
(request.isInternal() || // 内部按钮无条件停止
direction == request.getDirection() || // 同向外部请求
isAtBoundaryFloor()); // 顶层/底层强制转向
}
优化方向:
引入状态模式(如MovingState/StopState)替代硬编码条件判断
四、入口层(Main)
Main 系统入口
核心职责:程序启动与IO处理
初始化电梯参数(minFloor/maxFloor)
创建控制器实例并传递输入
监听终止命令("end")
设计缺陷:
直接使用Scanner耦合IO实现,难以切换输入源
改进方案:
java
public static void main(String[] args) {
InputSource input = new ConsoleInput(); // 可替换为FileInput
new ElevatorSystem(min, max).start(input);
}
五、类间协作关系
mermaid
flowchart TB
Main --> ElevatorController
ElevatorController --> RequestQueue
ElevatorController --> Elevator
RequestQueue --> PassengerRequest
Elevator --> Direction
classDef model fill:#f9f,stroke:#333;
classDef ctrl fill:#bbf,stroke:#333;
classDef device fill:#f96,stroke:#333;
class PassengerRequest,RequestQueue model;
class ElevatorController ctrl;
class Elevator,Direction device;
协作原则:
单向依赖:上层(Main)可依赖下层(Controller),反之禁止
接口隔离:队列通过getNextRequest()暴露最小接口
迪米特法则:Elevator不知道RequestQueue的存在
六、设计模式应用总结
组合模式
ElevatorController聚合Elevator和RequestQueue
优点:可独立扩展队列算法或电梯型号
不变模式
PassengerRequest的不可变性保证线程安全
潜在扩展模式
策略模式:动态切换调度算法(SCAN/LOOK等)
观察者模式:实现电梯位置实时监控
第七次题目集
指标------数值------健康度评估
类数量------5------职责划分清晰(Passenger/RequestQueue/Elevator等)
方法平均复杂度------3.2------较v2版本上升(因引入双向队列和更复杂调度逻辑),findNextRequest()达6需重构
代码行数------280------核心逻辑行数(新增楼层间移动策略)
注释比例------15%------方法级注释完整,但复杂算法缺少行内说明
耦合度------0.4------中等耦合(ElevatorController需感知两种队列细节)
相应心得:
- 设计模式应用
策略模式替换硬编码调度算法:
interface SchedulingStrategy {
Passenger findNextRequest(RequestQueue queue, Elevator elevator);
}
class LookStrategy implements SchedulingStrategy { ... }
状态模式管理电梯运行阶段:
interface ElevatorState {
void handleRequest(ElevatorContext context);
}
class MovingUpState implements ElevatorState { ... }
2. 性能优化
请求索引:使用TreeMap加速最近楼层查找
private NavigableMap<Integer, Passenger> upRequests = new TreeMap<>();
并行处理:分离IO线程与调度线程(需线程安全队列)
- 工程实践心得
领域建模的精准性
发现Passenger既表示请求又携带行程信息,应拆分为:
classDiagram
class CallRequest { +floor +direction }
class RideRequest { +targetFloor }
Passenger *-- CallRequest
Passenger *-- RideRequest
测试驱动开发的必要性
边界用例测试(如同时按下3楼UP和5楼DOWN)
性能测试:千级请求压力下的调度延迟
可观测性增强
添加运行指标统计:
class Metrics {
int totalRequests;
double avgWaitTime;
void logRequest(Passenger p) { ... }
}
类职责说明:
Passenger类:
封装乘客信息:源楼层、目的楼层、是否为内部请求
提供方向计算方法
RequestQueue类:
管理两个队列:外部请求队列和内部请求队列
处理请求添加、重复请求过滤
处理外部请求完成后自动添加内部请求
Elevator类:
管理电梯状态:当前楼层、运行方向
实现电梯移动、开关门操作
判断是否应该在当前楼层停止
ElevatorController类:
协调电梯和请求队列
实现电梯调度算法
处理请求优先级和方向选择
采坑心得
(对源码的提交过程中出现的问题及心得进行总结)
一、开发过程中遇到的问题
- 初始设计阶段的问题
问题1:类职责划分不清晰
最初版本将所有功能都放在Elevator类中,导致类过于庞大
调度逻辑、请求处理和电梯运行混杂在一起
违反了单一职责原则(SRP)
解决方式:
将系统拆分为Passenger、RequestQueue、Elevator和ElevatorController四个类
每个类只负责一个明确的职责
- 请求处理逻辑问题
问题2:重复请求处理不当
最初没有正确处理连续的相同请求
导致电梯会在同一楼层多次停留
解决方式:
在RequestQueue类中添加lastRequest字段
比较新请求与上一个请求,过滤掉完全相同的请求
- 电梯调度算法问题
问题3:方向选择策略不完善
初期版本在电梯空闲时方向选择不合理
有时会导致电梯来回"抖动"
解决方式:
实现更智能的方向选择算法
优先处理当前方向的请求,没有请求时才改变方向
- 外部请求转内部请求问题
问题4:外部请求处理后未自动添加内部请求
最初版本没有实现题目要求的"外部请求处理后要将其目的楼层加入内部队列"
解决方式:
在RequestQueue类中添加completeExternalRequest方法
处理外部请求后自动创建对应的内部请求
二、架构设计心得
- 单一职责原则的重要性
通过这次开发,我深刻体会到SRP的价值:
每个类只做一件事,使代码更易于理解和维护
修改一个功能时不会意外影响其他功能
测试时可以针对每个类单独测试
- 状态管理的艺术
电梯系统本质上是一个状态机,我学到了:
明确的状态划分(上行、下行、空闲)
状态转换的条件要清晰明确
当前楼层和方向是核心状态,需要精心维护
- 请求队列的设计
请求队列是系统的核心组件,我的体会是:
分离外部和内部队列更符合实际场景
队列操作要保证线程安全(虽然本题不涉及多线程)
合理的请求优先级策略对系统效率至关重要
三、代码质量提升经验
- 输入验证的必要性
最初版本对输入验证不足,导致程序可能崩溃
改进后对所有输入进行严格验证:
楼层范围检查
请求格式检查
方向值检查
- 异常处理的完善
从简单的忽略所有异常到有针对性的处理
对不同类型的错误采取不同策略:
无效楼层:忽略
格式错误:忽略
重复请求:过滤
- 日志输出的优化
初期输出信息不够详细,难以调试
最终版本提供了清晰的运行日志:
电梯移动时的楼层和方向
开关门的明确指示
便于验证程序正确性
四、测试经验总结
- 测试用例设计
边界测试:最小/最大楼层请求
特殊情况测试:连续相同请求
综合测试:混合内外请求
压力测试:大量请求连续输入
- 测试中发现的问题
电梯在顶层和底层时的方向处理错误
某些情况下会错过合理请求
外部请求转内部请求的实现不完整
- 测试自动化的重要性
初期手动测试效率低且容易遗漏
后期建立了简单的测试框架
考虑使用JUnit等单元测试框架会更好
改进建议
(对相应题目的编码改进给出自己的见解,做到可持续改进)
性能优化:
实现更高效的调度算法
考虑请求的时间因素(如高峰时段)
功能扩展:
支持多部电梯
增加超载检测
添加紧急停止功能
代码结构优化:
引入设计模式(如状态模式)
更好的抽象电梯行为
总结
(对本阶段三次题目集的综合性总结)
通过这个项目的多次迭代,我不仅提升了对面向对象设计的理解,也加深了对实际工程问题的认识。最大的收获是明白了良好的架构设计对软件质量的决定性影响。从最初将所有逻辑堆砌在一个类中,到最后清晰的四个类分工协作,代码的可读性、可维护性和可扩展性都得到了显著提升。
这个项目也让我认识到,软件开发是一个不断迭代和优化的过程,每次重构都能发现之前设计的不足,并找到更好的解决方案。这种持续改进的思维方式对成为一个优秀的软件工程师至关重要。