本博客目的为了总结OOP学习过程中,前三次作业的中的思考过程和修复bug的过程。
第一次作业:
7-5:NCHU_单部电梯调度程序
解决方法流程图:

这道题不需要使用面向对象的思路去求解,所以使用面向过程的思路求解即可。
题目分析
这道题要求模拟电梯的基本运行逻辑,包括处理内部请求(乘客在电梯内按下目标楼层)和外部请求(乘客在楼层按下上行/下行按钮)。电梯需要按照特定的调度规则运行,优先处理同方向的请求。
核心思路
我采用LOOK算法(电梯扫描算法)作为调度策略,这是电梯系统中常用的高效算法:
同方向优先:电梯沿当前方向移动时,优先服务该方向上的所有请求
反向扫描:当前方向无请求时,改变方向服务另一方向的请求
实时判断:每到达一个楼层都检查是否有需要停靠的请求
代码结构分析
主要数据结构
queue
queue
状态管理:记录电梯当前楼层、运行方向、最大最小楼层限制
关键函数
请求解析函数:
is_out():判断是内部还是外部请求
parse_in():解析内部请求格式"<楼层>"
parse_out():解析外部请求格式"<楼层,方向>"
电梯操作函数:
open_door():模拟开门关门操作
pass_by():输出经过楼层的信息
算法优化点
无效请求过滤:在解析请求时检查楼层是否在有效范围内
方向决策优化:综合考虑内外请求的方向一致性
实时状态更新:每处理一个请求后重新评估运行方向
示例分析
以题目样例为例:
请求序列:❤️,UP> → <5> → <6,DOWN> → <7> → <3>
电梯运行轨迹:
从1层出发,响应3层上行请求
途中响应5层内部请求(同方向)
继续上行响应7层内部请求
改变方向响应6层下行请求
最后响应3层内部请求
这体现了"同方向优先"的原则。
复杂度分析
时间复杂度:O(N × M),其中N为请求数,M为楼层数
空间复杂度:O(N),用于存储请求队列
总结
这个解法通过维护两个请求队列和实时方向判断,实现了电梯的基本调度功能。关键在于理解LOOK算法的"同方向优先"原则,并在代码中正确实现方向决策逻辑。
代码源码
点击查看代码
import java.util.*;
import java.util.regex.*;
public class ElevatorSimulation {
private static Queue<Integer> in_q = new LinkedList<>();
private static Queue<Pair> out_q = new LinkedList<>();
private static int max_t;
private static int min_t;
private static int happened_max_t = -1;
private static int happened_min_t = Integer.MAX_VALUE;
private static int globe_state = 1; // 一开始处于停止状态
private static int cf = 1; // 当前的楼层,初始为1
private static Map<String, Integer> states = new HashMap<String, Integer>() {{
put("UP", 1);
put("DOWN", 0);
put("STOP", 2);
}};
// 内部类用于表示楼层和方向的配对
static class Pair {
int first;
int second;
Pair(int first, int second) {
this.first = first;
this.second = second;
}
}
// 检查是否来自外部请求
private static boolean is_out(String x) {
return x.contains(",");
}
// 开门函数
private static void open_door(int x) {
System.out.printf("Open Door # Floor %d\n", x);
System.out.println("Close Door");
}
private static void pass_by(int x) {
System.out.printf("Current Floor: %d Direction: ", x);
String temp = globe_state == 0 ? "DOWN" : "UP";
System.out.println(temp);
}
private static int parse_in(String x) {
Pattern pattern = Pattern.compile("<(\\d+)>");
Matcher matcher = pattern.matcher(x);
if (matcher.find()) {
return Integer.parseInt(matcher.group(1));
}
return 0;
}
private static Pair parse_out(String x) {
Pattern pattern = Pattern.compile("<(\\d+),(\\w+)>");
Matcher matcher = pattern.matcher(x);
if (matcher.find()) {
int floor = Integer.parseInt(matcher.group(1));
String direction = matcher.group(2);
return new Pair(floor, states.get(direction));
}
return new Pair(0, 0);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取最小和最大楼层
min_t = scanner.nextInt();
max_t = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
String temp = "";
while (scanner.hasNextLine()) {
String k = scanner.nextLine().trim();
if (k.equals("end")) break;
if (k.equals(temp)) continue;
temp = k;
if (is_out(k)) {
Pair res = parse_out(k);
if (res.first < min_t || res.first > max_t) continue;
out_q.add(res);
happened_max_t = Math.max(happened_max_t, res.first);
happened_min_t = Math.min(happened_min_t, res.first);
} else {
int res = parse_in(k);
if (res < min_t || res > max_t) continue;
in_q.add(res);
happened_max_t = Math.max(happened_max_t, res);
happened_min_t = Math.min(happened_min_t, res);
}
}
scanner.close();
pass_by(1);
while (!in_q.isEmpty() || !out_q.isEmpty()) {
int fx = -1;
int in = Integer.MAX_VALUE;
int ou = Integer.MAX_VALUE;
if (in_q.isEmpty() && !out_q.isEmpty()) {
// 直接使用外面的并且改方向
Pair t = out_q.peek();
ou = t.first;
if (cf > t.first) {
globe_state = 0;
} else {
globe_state = 1;
}
fx = globe_state;
} else if (!in_q.isEmpty() && out_q.isEmpty()) {
// 里面就直接无条件了
int t = in_q.peek();
in = t;
if (cf > t) {
globe_state = 0;
} else {
globe_state = 1;
}
} else {
in = in_q.peek();
Pair outPair = out_q.peek();
ou = outPair.first;
fx = outPair.second;
int in_dirc = (in > cf) ? 1 : 0;
int out_dirc = (ou > cf) ? 1 : 0;
if (in_dirc == globe_state && out_dirc != globe_state) {
globe_state = in_dirc;
} else if (in_dirc != globe_state && out_dirc == globe_state) {
fx = globe_state;
} else {
int in_dis = Math.abs(in - cf);
int out_dis = Math.abs(ou - cf);
globe_state = in_dirc;
}
}
// 已经决定了方向,然后开始运行的逻辑
if (globe_state == 1) { // 上升
while (true) {
boolean stop = false;
if (cf == in) {
in_q.poll();
stop = true;
}
if (!out_q.isEmpty() && cf == ou && fx == globe_state) {
out_q.poll();
stop = true;
}
if (stop) {
open_door(cf);
break;
} else {
cf++;
pass_by(cf);
}
// 更新in和ou的值
in = in_q.isEmpty() ? Integer.MAX_VALUE : in_q.peek();
if (!out_q.isEmpty()) {
Pair p = out_q.peek();
ou = p.first;
fx = p.second;
} else {
ou = Integer.MAX_VALUE;
}
}
} else if (globe_state == 0) { // 下降
while (true) {
boolean stop = false;
if (cf == in) {
in_q.poll();
stop = true;
}
if (!out_q.isEmpty() && cf == ou && fx == globe_state) {
out_q.poll();
stop = true;
}
if (stop) {
open_door(cf);
break;
} else {
cf--;
pass_by(cf);
}
// 更新in和ou的值
in = in_q.isEmpty() ? Integer.MAX_VALUE : in_q.peek();
if (!out_q.isEmpty()) {
Pair p = out_q.peek();
ou = p.first;
fx = p.second;
} else {
ou = Integer.MAX_VALUE;
}
}
}
}
}
}
OOP2:类设计的电梯解决方案:
解决类图:

类设计的思想
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类
代码源码
点击查看代码
import java.util.*;
import java.util.regex.*;
// 电梯状态枚举
enum ElevatorState {
UP, DOWN, STOP
}
// 请求类型枚举
enum RequestType {
INTERNAL, EXTERNAL
}
// 请求类 - 负责表示电梯请求
class Request {
private final int floor;
private final ElevatorState direction;
private final RequestType type;
public Request(int floor, ElevatorState direction, RequestType type) {
this.floor = floor;
this.direction = direction;
this.type = type;
}
public int getFloor() { return floor; }
public ElevatorState getDirection() { return direction; }
public RequestType getType() { return type; }
@Override
public String toString() {
if (type == RequestType.INTERNAL) {
return "<" + floor + ">";
} else {
return "<" + floor + "," + direction + ">";
}
}
}
// 请求解析器类 - 负责解析输入字符串为请求对象
class RequestParser {
private final int minFloor;
private final int maxFloor;
public RequestParser(int minFloor, int maxFloor) {
this.minFloor = minFloor;
this.maxFloor = maxFloor;
}
public Request parse(String input) {
if (input.contains(",")) {
return parseExternalRequest(input);
} else {
return parseInternalRequest(input);
}
}
private Request parseInternalRequest(String input) {
Pattern pattern = Pattern.compile("<(\\d+)>");
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
int floor = Integer.parseInt(matcher.group(1));
if (isValidFloor(floor)) {
return new Request(floor, ElevatorState.STOP, RequestType.INTERNAL);
}
}
return null;
}
private Request parseExternalRequest(String input) {
Pattern pattern = Pattern.compile("<(\\d+),(\\w+)>");
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
int floor = Integer.parseInt(matcher.group(1));
ElevatorState direction = parseDirection(matcher.group(2));
if (isValidFloor(floor) && direction != null) {
return new Request(floor, direction, RequestType.EXTERNAL);
}
}
return null;
}
private ElevatorState parseDirection(String directionStr) {
try {
return ElevatorState.valueOf(directionStr.toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
private boolean isValidFloor(int floor) {
return floor >= minFloor && floor <= maxFloor;
}
}
// 请求队列类 - 负责管理请求队列
class RequestQueue {
private final Queue<Request> internalRequests;
private final Queue<Request> externalRequests;
public RequestQueue() {
this.internalRequests = new LinkedList<>();
this.externalRequests = new LinkedList<>();
}
public void addRequest(Request request) {
if (request.getType() == RequestType.INTERNAL) {
internalRequests.add(request);
} else {
externalRequests.add(request);
}
}
public Request peekNextInternal() {
return internalRequests.peek();
}
public Request peekNextExternal() {
return externalRequests.peek();
}
public Request pollNextInternal() {
return internalRequests.poll();
}
public Request pollNextExternal() {
return externalRequests.poll();
}
public boolean isEmpty() {
return internalRequests.isEmpty() && externalRequests.isEmpty();
}
public boolean hasInternalRequests() {
return !internalRequests.isEmpty();
}
public boolean hasExternalRequests() {
return !externalRequests.isEmpty();
}
public int getInternalRequestCount() {
return internalRequests.size();
}
public int getExternalRequestCount() {
return externalRequests.size();
}
}
// 电梯类 - 负责电梯的基本操作和状态管理
class Elevator {
private int currentFloor;
private ElevatorState state;
private final int minFloor;
private final int maxFloor;
public Elevator(int minFloor, int maxFloor) {
this.minFloor = minFloor;
this.maxFloor = maxFloor;
this.currentFloor = 1; // 默认从1楼开始
this.state = ElevatorState.STOP;
}
public int getCurrentFloor() { return currentFloor; }
public ElevatorState getState() { return state; }
public int getMinFloor() { return minFloor; }
public int getMaxFloor() { return maxFloor; }
public void moveUp() {
if (currentFloor < maxFloor) {
currentFloor++;
state = ElevatorState.UP;
}
}
public void moveDown() {
if (currentFloor > minFloor) {
currentFloor--;
state = ElevatorState.DOWN;
}
}
public void stop() {
state = ElevatorState.STOP;
}
public void openDoor() {
System.out.printf("Open Door # Floor %d\n", currentFloor);
}
public void closeDoor() {
System.out.println("Close Door");
}
public void passBy() {
System.out.printf("Current Floor: %d Direction: %s\n", currentFloor, state);
}
public boolean shouldStopForRequest(Request request) {
if (request.getType() == RequestType.INTERNAL) {
return request.getFloor() == currentFloor;
} else {
return request.getFloor() == currentFloor &&
(request.getDirection() == state || state == ElevatorState.STOP);
}
}
public int getDistanceToFloor(int floor) {
return Math.abs(currentFloor - floor);
}
public boolean isAbove(int floor) {
return currentFloor > floor;
}
public boolean isBelow(int floor) {
return currentFloor < floor;
}
}
// 调度策略接口
interface SchedulingStrategy {
Request chooseNextRequest(Elevator elevator, RequestQueue queue);
}
// 基础调度策略类
class BasicSchedulingStrategy implements SchedulingStrategy {
@Override
public Request chooseNextRequest(Elevator elevator, RequestQueue queue) {
Request nextInternal = queue.peekNextInternal();
Request nextExternal = queue.peekNextExternal();
if (nextInternal == null && nextExternal == null) {
return null;
}
if (nextInternal == null) {
return nextExternal;
}
if (nextExternal == null) {
return nextInternal;
}
// 简单策略:选择距离更近的请求
int internalDistance = elevator.getDistanceToFloor(nextInternal.getFloor());
int externalDistance = elevator.getDistanceToFloor(nextExternal.getFloor());
return internalDistance <= externalDistance ? nextInternal : nextExternal;
}
}
// 电梯控制器类 - 负责协调电梯运行和请求处理
class ElevatorController {
private final Elevator elevator;
private final RequestQueue requestQueue;
private final SchedulingStrategy schedulingStrategy;
public ElevatorController(Elevator elevator, RequestQueue requestQueue,
SchedulingStrategy schedulingStrategy) {
this.elevator = elevator;
this.requestQueue = requestQueue;
this.schedulingStrategy = schedulingStrategy;
}
public void processRequests() {
elevator.passBy(); // 初始状态
while (!requestQueue.isEmpty()) {
Request nextRequest = schedulingStrategy.chooseNextRequest(elevator, requestQueue);
if (nextRequest != null) {
moveToRequest(nextRequest);
processStop();
}
}
}
private void moveToRequest(Request request) {
// 确定方向
if (elevator.getCurrentFloor() < request.getFloor()) {
elevator.moveUp();
} else if (elevator.getCurrentFloor() > request.getFloor()) {
elevator.moveDown();
}
// 移动到目标楼层
while (elevator.getCurrentFloor() != request.getFloor()) {
elevator.passBy();
if (elevator.getCurrentFloor() < request.getFloor()) {
elevator.moveUp();
} else {
elevator.moveDown();
}
}
}
private void processStop() {
elevator.stop();
elevator.openDoor();
// 处理当前楼层的所有相关请求
removeServedRequests();
elevator.closeDoor();
}
private void removeServedRequests() {
// 移除内部请求
Request internalRequest = requestQueue.peekNextInternal();
while (internalRequest != null && elevator.shouldStopForRequest(internalRequest)) {
requestQueue.pollNextInternal();
internalRequest = requestQueue.peekNextInternal();
}
// 移除外部请求
Request externalRequest = requestQueue.peekNextExternal();
while (externalRequest != null && elevator.shouldStopForRequest(externalRequest)) {
requestQueue.pollNextExternal();
externalRequest = requestQueue.peekNextExternal();
}
}
public void addRequest(Request request) {
requestQueue.addRequest(request);
}
}
// 主程序类
public class ElevatorSimulation {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取最小和最大楼层
int minFloor = scanner.nextInt();
int maxFloor = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
// 创建组件
Elevator elevator = new Elevator(minFloor, maxFloor);
RequestQueue requestQueue = new RequestQueue();
SchedulingStrategy strategy = new BasicSchedulingStrategy();
ElevatorController controller = new ElevatorController(elevator, requestQueue, strategy);
RequestParser parser = new RequestParser(minFloor, maxFloor);
// 读取和处理输入
String previousLine = "";
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (line.equals("end")) break;
if (line.equals(previousLine)) continue;
previousLine = line;
Request request = parser.parse(line);
if (request != null) {
controller.addRequest(request);
}
}
scanner.close();
// 开始模拟
controller.processRequests();
}
}
代码分析
设计思路
本次重构将原有的过程式代码重构为面向对象设计,遵循单一职责原则(SRP),将系统分解为多个高内聚、低耦合的类。
核心设计理念
- 职责分离:每个类只负责一个明确的功能领域
- 面向接口编程:通过接口定义契约,提高扩展性
- 数据封装:隐藏内部实现细节,提供清晰的API
类结构设计
- 枚举类型 - 定义系统常量
enum ElevatorState { UP, DOWN, STOP }
enum RequestType { INTERNAL, EXTERNAL }
职责:定义系统的状态和类型常量,提高代码可读性和类型安全性。
- Request类 - 请求数据模型
职责:封装请求数据,提供不可变的数据对象。
设计要点:
• 使用final字段确保不可变性
• 清晰的getter方法访问数据
• 重写toString()用于调试输出
- RequestParser类 - 输入解析器
职责:将原始字符串输入解析为Request对象。
设计要点:
• 使用正则表达式进行模式匹配
• 验证楼层范围的有效性
• 处理解析异常情况
- RequestQueue类 - 请求队列管理
职责:分别管理内部和外部请求队列。
设计要点:
• 使用两个独立队列分别存储内部和外部请求
• 提供统一的队列操作接口
• 支持队列状态查询
- Elevator类 - 电梯实体
职责:封装电梯的物理属性和基本操作。
核心方法:
• moveUp()/moveDown() - 电梯移动
• openDoor()/closeDoor() - 门控制
• shouldStopForRequest() - 停止条件判断
- SchedulingStrategy接口 - 调度策略契约
职责:定义调度算法的统一接口。
设计优势:
• 支持多种调度算法实现
• 易于扩展新的调度策略
• 符合开闭原则
- ElevatorController类 - 系统协调器
职责:协调各组件工作,实现核心业务逻辑。
核心流程:
-
选择下一个请求
-
移动电梯到目标楼层
-
处理停止和请求服务
-
ElevatorSimulation类 - 程序入口
职责:初始化系统组件,启动模拟流程。
设计模式应用
策略模式(Strategy Pattern)
通过SchedulingStrategy接口,可以轻松实现不同的调度算法:
// 可以实现的策略示例
class LookSchedulingStrategy implements SchedulingStrategy { ... }
class ScanSchedulingStrategy implements SchedulingStrategy { ... }
工厂方法模式(Factory Method)
RequestParser作为简单的工厂,根据输入创建相应的Request对象。
算法流程
开始
↓
读取楼层范围配置
↓
初始化各组件(电梯、队列、解析器、控制器)
↓
循环读取输入并解析为请求对象
↓
控制器处理所有请求:
↓
while (队列非空)
↓
选择下一个请求(调度策略)
↓
移动电梯到目标楼层
↓
处理当前楼层的所有匹配请求
↓
更新队列状态
↓
结束
代码优势
1. 可维护性
• 每个类职责单一,修改影响范围小
• 清晰的类边界,降低耦合度
2. 可扩展性
• 易于添加新的调度算法
• 支持新的请求类型扩展
• 电梯行为可定制化
3. 可测试性
• 每个类可以独立单元测试
• 模拟依赖关系简单
4. 可读性
• 清晰的类和方法命名
• 逻辑分层明确
使用示例
点击查看代码
// 创建组件
Elevator elevator = new Elevator(1, 10);
RequestQueue queue = new RequestQueue();
SchedulingStrategy strategy = new BasicSchedulingStrategy();
ElevatorController controller = new ElevatorController(elevator, queue, strategy);
// 添加请求
点击查看代码
controller.addRequest(new Request(5, ElevatorState.UP, RequestType.EXTERNAL));
controller.addRequest(new Request(3, ElevatorState.STOP, RequestType.INTERNAL));
// 运行模拟
点击查看代码
controller.processRequests();
总结
本次重构成功将过程式代码转化为面向对象设计,通过合理的职责分配和接口设计,使系统具备了良好的可维护性、可扩展性和可测试性。这种设计为后续功能扩展(如多电梯调度、图形界面等)奠定了坚实基础。
浙公网安备 33010602011771号