smartzwz

导航

 

本博客目的为了总结OOP学习过程中,前三次作业的中的思考过程和修复bug的过程。

第一次作业:

7-5:NCHU_单部电梯调度程序
解决方法流程图:

电梯调度程序流程图

这道题不需要使用面向对象的思路去求解,所以使用面向过程的思路求解即可。
题目分析
这道题要求模拟电梯的基本运行逻辑,包括处理内部请求(乘客在电梯内按下目标楼层)和外部请求(乘客在楼层按下上行/下行按钮)。电梯需要按照特定的调度规则运行,优先处理同方向的请求。
核心思路
我采用LOOK算法(电梯扫描算法)作为调度策略,这是电梯系统中常用的高效算法:
同方向优先:电梯沿当前方向移动时,优先服务该方向上的所有请求
反向扫描:当前方向无请求时,改变方向服务另一方向的请求
实时判断:每到达一个楼层都检查是否有需要停靠的请求
代码结构分析
主要数据结构
queue in_q:电梯内部请求队列(目标楼层)
queue out_q:电梯外部请求队列(楼层,方向)
状态管理:记录电梯当前楼层、运行方向、最大最小楼层限制
关键函数
请求解析函数:
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:类设计的电梯解决方案:
解决类图:

b719ebfe-be60-4c61-9e07-eb633d86e799

类设计的思想
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(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),将系统分解为多个高内聚、低耦合的类。

核心设计理念

  1. 职责分离:每个类只负责一个明确的功能领域
  2. 面向接口编程:通过接口定义契约,提高扩展性
  3. 数据封装:隐藏内部实现细节,提供清晰的API

类结构设计

  1. 枚举类型 - 定义系统常量

enum ElevatorState { UP, DOWN, STOP }
enum RequestType { INTERNAL, EXTERNAL }

职责:定义系统的状态和类型常量,提高代码可读性和类型安全性。

  1. Request类 - 请求数据模型

职责:封装请求数据,提供不可变的数据对象。

设计要点:
• 使用final字段确保不可变性

• 清晰的getter方法访问数据

• 重写toString()用于调试输出

  1. RequestParser类 - 输入解析器

职责:将原始字符串输入解析为Request对象。

设计要点:
• 使用正则表达式进行模式匹配

• 验证楼层范围的有效性

• 处理解析异常情况

  1. RequestQueue类 - 请求队列管理

职责:分别管理内部和外部请求队列。

设计要点:
• 使用两个独立队列分别存储内部和外部请求

• 提供统一的队列操作接口

• 支持队列状态查询

  1. Elevator类 - 电梯实体

职责:封装电梯的物理属性和基本操作。

核心方法:
• moveUp()/moveDown() - 电梯移动

• openDoor()/closeDoor() - 门控制

• shouldStopForRequest() - 停止条件判断

  1. SchedulingStrategy接口 - 调度策略契约

职责:定义调度算法的统一接口。

设计优势:
• 支持多种调度算法实现

• 易于扩展新的调度策略

• 符合开闭原则

  1. ElevatorController类 - 系统协调器

职责:协调各组件工作,实现核心业务逻辑。

核心流程:

  1. 选择下一个请求

  2. 移动电梯到目标楼层

  3. 处理停止和请求服务

  4. 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();

总结

本次重构成功将过程式代码转化为面向对象设计,通过合理的职责分配和接口设计,使系统具备了良好的可维护性、可扩展性和可测试性。这种设计为后续功能扩展(如多电梯调度、图形界面等)奠定了坚实基础。

posted on 2025-11-22 10:34  NCHU_23207131  阅读(0)  评论(0)    收藏  举报