java面向对象思维程序设计开发以及案例 -电梯运行问题对象分析与程序设计(2)

简介: java面向对象思维程序设计开发以及案例 -电梯运行问题对象分析与程序设计(2)

承接上文《电梯运行问题对象分析与程序设计(1)》


上节基本完成了一个半成品电梯程序,还有很多不足,比如下面,下面是上节的运行结果部分输出

初始化了一个18层的电梯,当前电梯在第1层

电梯启动

当前在电梯第2层

人员进入,按了13层

当前在电梯第3层

当前在电梯第4层

当前在电梯第5层

人员进入,按了12层

当前在电梯第6层

当前在电梯第7层

当前在电梯第8层

人员进入,按了6层

当前在电梯第9层

当前在电梯第10层

当前在电梯第11层

人员进入,按了3层

当前在电梯第12层

12人员出门

剩余楼层[3, 6, 13]

人员进入,按了10层

人员进入,按了14层

当前在电梯第13层

13人员出门

剩余楼层[3, 6, 10, 14]


期望结果


之前说过我们的运行流程尽量贴近我们的时序图

1.png

因此,我想要的是以下期望输出结果是下面这样:


初始化了一个18层的电梯,当前电梯在第1层

电梯启动

当前在电梯第2层,向上运行中

5楼有人按了向下的电梯,

电梯达到5楼,由于方向相反,继续运行

电梯达到5楼,停止,开门等待10s

5楼人员进入电梯,按了2楼

电梯达到2楼,停止10s,开门等待10s


测试驱动思维


什么事测试驱动思维


就是我们预先定义好我们要的结果,反向去开发程序,让程序执行结果和我们期望结果一致即可,类似单元测试一样,先写单元测试,后填入代码内容


比如我上面的输出,虽然还没写程序,但我就是要这个结果,不管你程序咋实现的,给我这个结果就好了


上节讨论后的对象设计图


1.png

按照我们上节的优化对象设计,添加floor对象 ,好处如下


1.期望可以用floor代码某层楼,封装对该楼层的信息

比如某层是否有人按了按钮等待进入,电梯里是否有人按了按钮想要达到某层出去

2.便于扩展,以后对楼层进行扩展功能只需要修改floor 对象即可

本篇主要是用了一个map 去收集floor 对象,其实这样是不对的,

不应该把对楼层的处理逻辑暴露给电梯对象或者外部,

应该定义一个控制器controller 去封装对floor对象的内部逻辑处理

从实际场景还是扩展性来说这都是最好的,如下是优化后的领域对象图

1.png

优化后代码如下


public class ElevatorTest2 {
    //是否是测试环境,测试环境会有一些测试方法运行,比如进入电梯后人员选择了某层
    static boolean isTest = true;
     //18层的电梯
    static int floorSize = 18;
    //电梯初始化
    static final Elevator2 elevator = new Elevator2(floorSize);
    public static void main(String[] args) {
        //创建一个线程池,初始化两个线程,1:电梯运行线程,2:模拟随机层数人员进入电梯线程
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue(2));
        //启动电梯
        threadPoolExecutor.execute(elevator);
        //模拟随机楼层人员进电梯与出电梯
        threadPoolExecutor.execute(() -> {
            while (true) {
                //模拟随机生成 1~18楼楼层的人
                int currentFloorNumber = new Random().nextInt(18);
                currentFloorNumber++;
                //模拟按向上或者向下
                int direction = new Random().nextInt(2);
                direction++;
                try {
                    //创建一个随机楼层的人按下了向上或者向下对象 floor
                    if (currentFloorNumber != floorSize && direction == 1) {
                        //按下向上按钮
                        elevator.pressUp(currentFloorNumber);
                        Thread.sleep(10000);
                    } else if (currentFloorNumber != 1 && direction == 2) {
                        //按下向下按钮
                        elevator.pressDown(currentFloorNumber);
                        Thread.sleep(10000);
                    }
                } catch (InterruptedException e) {
                }
            }
        });
    }
}
class Floor {
    //楼层号
    int floorNumber = 0;
    //运行方向 true为向上
    boolean directionTop = false;
    //是否该层有人需要进入
    boolean isEntered = false;
    //是否该层有人需要出去
    boolean isTarget = false;
    public Floor(int floorNumber) {
        this.floorNumber = floorNumber;
    }
    public void setDirectionUp() {
        this.directionTop = true;
    }
    public void setDirectionDown() {
        this.directionTop = false;
    }
    public void setEntered(boolean entered) {
        isEntered = entered;
    }
    public void setTarget(boolean target) {
        isTarget = target;
    }
    public boolean isDirectionTop() {
        return directionTop;
    }
    public boolean isEntered() {
        return isEntered;
    }
    public boolean isTarget() {
        return isTarget;
    }
}
class Elevator2 implements Runnable {
    boolean start = false;
    int currentFloor;
    boolean directionTop = true;
    int floor;
    //保存目标层数map,最好是变为一个对象,叫做控制器controller对象,封装对楼层的一些操作,尽量避免对外暴露方法
    Map<Integer, Floor> floorsMap = new HashMap<>(18);
    public Elevator2(int floor) {
        this.floor = floor;
        this.currentFloor = 1;
        System.out.println("初始化了一个" + floor + "层的电梯,当前电梯在第1层");
        IntStream.range(1, floor+1).forEach(num -> {
            Floor fl = new Floor(num);
            floorsMap.put(num, fl);
        });
    }
    /**
     * 是否是顶层
     */
    public boolean isTop() {
        return this.currentFloor == floor;
    }
    /**
     * 是否是底层
     */
    public boolean isBottom() {
        return this.currentFloor == 1;
    }
    @Override
    public void run() {
        System.out.println("电梯启动");
        start = true;
        while (start) {
            try {
                if (directionTop) {
                    currentFloor++;
                    System.out.println("当前在电梯第" + currentFloor + "层");
                } else {
                    currentFloor--;
                    System.out.println("当前在电梯第" + currentFloor + "层");
                }
                if (isTop()) {
                    directionTop = false;
                    System.out.println("到达顶层,开始向下运行");
                }
                if (isBottom()) {
                    directionTop = true;
                    System.out.println("到达1层,开始向上运行");
                }
                //判断该层是否有人需要进入
                if (floorsMap.get(currentFloor).isEntered()) {
                    Floor fl = floorsMap.get(currentFloor);
                    if (directionTop == fl.isDirectionTop()) {
                        System.out.println("到达" + currentFloor + "层,开门,等待10s,等待人员进入");
                        fl.setEntered(false);
                        //测试的话模拟人员进入后按下层数选择按钮,实际上select方法应该是独立的对外电梯内人员异步选择的
                        if(ElevatorTest2.isTest){
                            //模拟人电梯内选择的目的地层数
                            int targetFloorNumber = new Random().nextInt(18);
                            targetFloorNumber++;
                            ElevatorTest2.elevator.select(targetFloorNumber);
                        }
                        Thread.sleep(10000);
                    }else {
                        System.out.println("到达" + currentFloor + "层,与按电梯人方向相反,继续运行");
                        Thread.sleep(1000);
                    }
                }
                //判断该层是否有人需要出去
                else if(floorsMap.get(currentFloor).isTarget()){
                    System.out.println("到达" + currentFloor + "层,开门,等待10s,等待人员出门");
                    floorsMap.get(currentFloor).setTarget(false);
                    Thread.sleep(10000);
                }
                else {
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
            }
        }
    }
    /**
     * 电梯门口按下向上按钮 传入当前按钮所在楼层号
     * */
    public void pressUp(Integer floor) {
        Floor fl = floorsMap.get(floor);
        fl.setDirectionUp();
        fl.setEntered(true);
        System.out.println(floor + "层人员按了向上的按钮");
    }
    /**
     * 电梯内按下选择按钮 传入想要出去楼层号
     * */
    public void select(Integer targetFloor) {
        Floor fl = floorsMap.get(targetFloor);
        fl.setTarget(true);
        System.out.println("进入电梯后人员按了第"+targetFloor+"层的按钮");
    }
    /**
     * 电梯门口按下向下按钮 传入当前按钮所在楼层号
     * */
    public void pressDown(Integer floor) {
        Floor fl = floorsMap.get(floor);
        fl.setDirectionDown();
        fl.setEntered(true);
        System.out.println(floor + "层人员按了向下的按钮");
    }
}

输出结果如下,符合预期:


初始化了一个18层的电梯,当前电梯在第1层

电梯启动

当前在电梯第2层

1层人员按了向上的按钮

当前在电梯第3层

当前在电梯第4层

当前在电梯第5层

当前在电梯第6层

当前在电梯第7层

当前在电梯第8层

当前在电梯第9层

当前在电梯第10层

当前在电梯第11层

1层人员按了向上的按钮

当前在电梯第12层

当前在电梯第13层

当前在电梯第14层

当前在电梯第15层

当前在电梯第16层

当前在电梯第17层

当前在电梯第18层

到达顶层,开始向下运行

当前在电梯第17层

当前在电梯第16层

当前在电梯第15层

16层人员按了向下的按钮

当前在电梯第14层

当前在电梯第13层

当前在电梯第12层

当前在电梯第11层

当前在电梯第10层

当前在电梯第9层

当前在电梯第8层

当前在电梯第7层

当前在电梯第6层

当前在电梯第5层

12层人员按了向下的按钮

当前在电梯第4层

当前在电梯第3层

当前在电梯第2层

当前在电梯第1层

到达1层,开始向上运行

到达1层,开门,等待10s,等待人员进入

进入电梯后人员按了第16层的按钮

17层人员按了向上的按钮

当前在电梯第2层

当前在电梯第3层

当前在电梯第4层

当前在电梯第5层

当前在电梯第6层

当前在电梯第7层

当前在电梯第8层

12层人员按了向下的按钮

当前在电梯第9层

当前在电梯第10层

当前在电梯第11层

当前在电梯第12层

到达12层,与按电梯人方向相反,继续运行

当前在电梯第13层

当前在电梯第14层

当前在电梯第15层

当前在电梯第16层

到达16层,与按电梯人方向相反,继续运行

当前在电梯第17层

到达17层,开门,等待10s,等待人员进入

进入电梯后人员按了第8层的按钮

15层人员按了向上的按钮

当前在电梯第18层

到达顶层,开始向下运行

当前在电梯第17层

17层人员按了向上的按钮

当前在电梯第16层

到达16层,开门,等待10s,等待人员进入

进入电梯后人员按了第1层的按钮

3层人员按了向上的按钮

当前在电梯第15层

到达15层,与按电梯人方向相反,继续运行

当前在电梯第14层

当前在电梯第13层

当前在电梯第12层

到达12层,开门,等待10s,等待人员进入

进入电梯后人员按了第12层的按钮

14层人员按了向下的按钮

当前在电梯第11层

当前在电梯第10层

当前在电梯第9层

当前在电梯第8层

到达8层,开门,等待10s,等待人员出门

4层人员按了向下的按钮

当前在电梯第7层

当前在电梯第6层

当前在电梯第5层

当前在电梯第4层

到达4层,开门,等待10s,等待人员进入

进入电梯后人员按了第8层的按钮

12层人员按了向上的按钮

当前在电梯第3层

到达3层,与按电梯人方向相反,继续运行

8层人员按了向下的按钮

当前在电梯第2层

当前在电梯第1层

到达1层,开始向上运行

到达1层,开门,等待10s,等待人员出门

5层人员按了向下的按钮

当前在电梯第2层

当前在电梯第3层

到达3层,开门,等待10s,等待人员进入

进入电梯后人员按了第1层的按钮

16层人员按了向下的按钮

当前在电梯第4层

当前在电梯第5层

到达5层,与按电梯人方向相反,继续运行


思考题


1.完善改代码,添加控制器对象代替map封装floors

2.完善业务逻辑,添加判断如果进入电梯后按了和当前层一样的楼层号,给出重新选择提示

相关文章
|
27天前
|
Java 测试技术 开发者
💡Java 零基础:彻底掌握 for 循环,打造高效程序设计
【10月更文挑战第15天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
109 63
|
22天前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
25天前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
45 17
|
19天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
53 3
|
21天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
40 2
|
25天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
22天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
14 2
|
27天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
17 1
|
25天前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
24 0
|
26天前
|
存储 Java 编译器
[Java]基本数据类型与引用类型赋值的底层分析
本文详细分析了Java中不同类型引用的存储方式,包括int、Integer、int[]、Integer[]等,并探讨了byte与其他类型间的转换及String的相关特性。文章通过多个示例解释了引用和对象的存储位置,以及字符串常量池的使用。此外,还对比了String和StringBuilder的性能差异,帮助读者深入理解Java内存管理机制。
19 0
下一篇
无影云桌面