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

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

电梯是我们日常生活中经常看见和使用的运载工具,但其中也隐藏着一个精而小的程序,我们今天模拟一个电梯运行程序来开始我们面向对象之旅


电梯问题-抽取关键需求


1.电梯首先肯定是要可以上下不间断运行

2.可以达到顶层或底层之后向反方向继续运行

3.人员可以按钮然后等待电梯停在本楼层并进入

4.进入的人可以选择自己的目的层数

5.电梯可以在目的层数停止等待一定时间后继续运行


问题需求关键环节流程图


我们进入需求转化环境,我们来梳理一下需求并且转化为一个时序图,便于我们拆分对象以及确定逻辑顺序,流程图如下

1.png

初步领域对象分析,最小可用原则


我们创建对象以及方法要使用最小可用原则,既不需要的就不要加进对象,直到确定需要这个对象或  者属性或者方法再添加,所以可以如下分析

依据时序图,我们初步分为了对象人person与对象电梯elevator

对象电梯可以被启动,然后不断运行,所以我们觉得他应该有一个start方法

我们也许会觉得对象人是外部触发电梯start 方法的对象

电梯应该方法是判断是否到顶部isTop(),是否到底部isBottom(),并且有一个方法是wait() 等待人进入并且选择目标层数selectFloor(int floor)

那么我们的对象建模就结束了,领域建模图如下

1.png

对象建模思考


思考1:电梯运行是否内部维护运行状态,是否需要一个start方法


我个人认为都可以,这里我是按照不需要设计的,我这里把电梯运行和电梯本身认为是两个实体,你 可以用轿厢品牌1 带动电梯,第二天也可以用轿厢2带动电梯,

也就是说电梯是被外部轿厢发动机带动运行的,而电梯本身只负责内部运行逻辑处理

换句话说我的运行创建线程并不是电梯内部维护的,而是适用方,可以用线程池,也可以直接new thread

这样就把更大的自由度留给了电梯合作方:电机轿厢,也符合易扩展多方合作的共赢思维


思考2:是否需要人这个person对象


本程序我们真的需要人这个对象吗,

实际上人在这里的作用无非就是提供电梯需要停止的层数

而实际上这个程序的外部使用者是真正活着的实体人

因此,我们在程序里定义人这个对象是多余的,所以我们可以将对象人person这个类去掉,换成层数floor

重新分析后-最终领域对象图

1.png

面向对象程序设计核心思想原则


1.就是让人从外面看起来就知道你的代码是在干什么,每一个对象都要有意义,并且真实符合世界上实体事物运行规则

2.设计的对象没有绝对的对与错,只要让人觉得你的对象确实合理并且使用起来符合正常人的思维习惯就可以

经过以上分析建模,形成目前的初版代码如下

public class ElevatorTest {
    public static void main(String[] args){
        //18层的电梯
        int floor = 18;
        //电梯初始化
        Elevator elevator = new Elevator(floor);
        //创建一个线程池,初始化两个线程,1:电梯运行线程,2:模拟随机层数人员进入电梯线程
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,2,10L, TimeUnit.SECONDS,new  ArrayBlockingQueue(2));
        //启动电梯
        threadPoolExecutor.execute(elevator);
        //模拟随机楼层人员进电梯与出电梯
        threadPoolExecutor.execute(()->{
                while (true){
                    //模拟随机生成 1~18楼人
                    int enterRandom = new Random().nextInt(18);
                    if(enterRandom ==0){
                        continue;
                    }else {
                        try {
                            //人员进入
                            elevator.enter(enterRandom);
                            Thread.sleep(3000);
                        }catch (InterruptedException e){
                        }
                    }
                }
        });
    }
}
class Elevator implements Runnable {
    boolean start = false;
    int currentFloor ;
    boolean directionTop = true;
    int floor ;
    //保存目标层数map
    Map enterMap = new HashMap(18);
    ReentrantLock lock = new ReentrantLock();
    public Elevator(int floor){
        this.floor = floor;
        this.currentFloor = 1;
        System.out.println("初始化了一个"+floor+"层的电梯,当前电梯在第1层");
    }
    /**
     * 人员进入电梯同时选择好目标层数
     * */
    public void enter(int outFloor){
        lock.lock();
        System.out.println("人员进入,按了"+outFloor+"层");
        enterMap.put(outFloor,null);
        lock.unlock();
    }
    /**
     * 是否是顶层
     * */
    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){
            if(!lock.isLocked()) {
                try {
                    if (directionTop) {
                        currentFloor++;
                        System.out.println("当前在电梯第" + currentFloor + "层");
                        //该层人员出门出门后将该层移除
                        if(enterMap.containsKey(currentFloor)){
                            try {
                                System.out.println(currentFloor+"人员出门");
                                enterMap.remove(currentFloor);
                                System.out.println("剩余楼层"+enterMap.keySet());
                                Thread.sleep(5000);
                            }catch (Exception e){
                            }
                        }
                        if (isTop()) {
                            directionTop = false;
                            System.out.println("到达顶层,开始向下运行");
                        }
                    } else {
                        currentFloor--;
                        System.out.println("当前在电梯第" + currentFloor + "层");
                        //该层人员出门出门后将该层移除
                        if(enterMap.containsKey(currentFloor)){
                            try {
                                System.out.println(currentFloor+"人员出门");
                                enterMap.remove(currentFloor);
                                System.out.println("剩余楼层"+enterMap.keySet());
                                Thread.sleep(5000);
                            }catch (Exception e){
                            }
                        }
                        System.out.println("当前在电梯第" + currentFloor + "层");
                        if (isBottom()) {
                            directionTop = true;
                            System.out.println("到达1层,开始向向运行");
                        }
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }else {
                try {
                    System.out.println("进人中。。");
                    Thread.sleep(5000);
                }catch (Exception e){
                }
            }
        }
    }
}

运行输出结果


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

电梯启动

当前在电梯第2层

人员进入,按了17


当前在电梯第3层

当前在电梯第4层

当前在电梯第5层

人员进入,按了13


当前在电梯第6层

当前在电梯第7层

当前在电梯第8层

人员进入,按了7


当前在电梯第9层

当前在电梯第10层

当前在电梯第11层

人员进入,按了6


当前在电梯第12层

当前在电梯第13层

13人员出门

剩余楼层[6, 7, 17]

人员进入,按了11


人员进入,按了4


当前在电梯第14层

人员进入,按了13


当前在电梯第15层

当前在电梯第16层

当前在电梯第17层

17人员出门

剩余楼层[4, 6, 7, 11, 13]

人员进入,按了7


人员进入,按了1


当前在电梯第18层

到达顶层,开始向下运行

人员进入,按了16


当前在电梯第17层

当前在电梯第17层

当前在电梯第16层

16人员出门

剩余楼层[1, 4, 6, 7, 11, 13]

人员进入,按了4


人员进入,按了3


当前在电梯第16层

当前在电梯第15层

当前在电梯第15层

当前在电梯第14层

当前在电梯第14层

人员进入,按了14


当前在电梯第13层

13人员出门

剩余楼层[1, 3, 4, 6, 7, 11, 14]

人员进入,按了10


当前在电梯第13层

人员进入,按了10


当前在电梯第12层

当前在电梯第12层

当前在电梯第11层

11人员出门

剩余楼层[1, 3, 4, 6, 7, 10, 14]


面向对象到底是节省时间还是浪费时间


其实使用面向对象角度建模并且进行程序开发并不是耗费时间,而是节省时间

因为设计的对象是充血模型,所以后续其他业务使用对应功能只需要对象.方法就可以了,这也是DDD 领域驱动设计的核心思想

而且面向对象设计开发也是利于大家对需求转化的加深,会加速开发效率,比如这个程序我建模可能用了30分钟,但编码只用了15分钟左右


结语


程序是依照我们小设计原则没有任何优化重构编写的,肯定细节上还有容错以及重用上有很多问题

但这就是程序设计,因为他满足了最小化的我们的需求

但是,问题终究是问题,我们还是要解决的,先把当前代码重构,然后我们要把缺失逻辑补充:

比如并没有实现人按电梯上下进入的操作,进入的人并没有实现同时允许多层数选择的操作等

没有使用层数floor对象,而是用map简单表示

下一篇我们将继续优化并完成这个电梯程序


相关文章
|
2天前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
6天前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
31 17
|
1天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
6 2
|
5天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
2天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
8 2
|
5天前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
6 0
|
5天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
74 38
|
2天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
6天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
20 1
[Java]线程生命周期与线程通信
|
4天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。