交通灯管理系统III

来源:互联网 发布:名人对人工智能的看法 编辑:程序博客网 时间:2024/06/08 06:04

交通灯管理系统III

 交通灯控制器类程序编写

测试类程序编写

 总体分析总结

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------

1.    交通灯控制器类LampController程序编写

1). 编写过程中注意事项

(1). 切换线路之间的交通灯的方法

每隔10s切换一下交通灯。

(2). 切换线路之间的交通灯的方法的简化

这个方法仅仅被使用一次之后即可,不需要被其他的方法调用。因此简化程序,就直接放入到LampController类的构造方法即可。

注意1】这个方法是可以单独拿出来的。只不过单独拿出来之后,newLampController()之后,还要单独调用这个方法才行。所以就直接简化到LampController这个类的构造方法中。

注意2】由于构造方法里面就是就是对成员属性的初始化,所以一定要对内部的表示当前是绿灯进行指定。由于是循环一直切换,所以随意指定从某个线路交通灯开始启动变亮即可

(3). LampController 类的设计规范

整个系统只能有一套交通灯控制系统,所以LampController类设计成单例模式。

(3). LampController类构造方法设计思路

[1]. 先设计成单线程的构造方法

先制定成员“当前亮绿灯的交通灯”是哪个交通灯(初始化成员对象)  ---->每隔10秒将当前灯变红,并按顺序将下一个方向的灯变绿

写成单线程程序如下:

private LampController(){      //初始化成员变量currentLamp这个成员变量 ---- 指定第一盏亮的交通灯      currentLamp =Lamp.S2N;      currentLamp.light();           while(true){            try{                  Thread.sleep(10*1000);//10s做一次切换            }catch(InterruptedException e){                  e.printStackTrace();            }            //变红一盏灯之后,就把它对应的下一盏灯变绿            currentLamp =currentLamp.blackOut();      }}

[2]. 分析这个单线程程序存在的问题

主线程在实例化LampController这个类的实例的时候,由于后面是死循环,那么就会造成主线程卡在这一直在进行交通灯之间的切换。假设此时实例化LampController之后,还有别的代码要执行,那么就无法执行到了。所以为了避免主线程被卡住,这个地方应该分出来给新的线程来执行。

[3]. 单线程程序的多线程化的图示


[4]. 每隔10s的切换交通灯实现步骤

{1}. 这里仍然采用线程的新技术来替代传统技术

{1}1. 使用的类和方法

通过java.util.concurrent.Executor类的静态方法newScheduledThreadPool产生可以按照一个固定的时间来执行Runnable实现类实例的线程池的方法,创建一个可以按照固定时间间隔执行任务的线程池(ScheduledExecutorService接口类型),这个方法产生的线程池中仅仅含有一个线程。 ----- 这里面就使用一个线程去执行多线程任务

{2}2. 通过可以以固定时间间隔执行任务的线程池(ScheduledExecutorService类的实例)的scheduleAtFixedRate方法来执行Runnable实现子类实例中run方法的内容。 -----scheduleAtFixedRate方法相当于原来的线程实例的start方法

{2}3. 在使用传统线程技术的时候,我们使用Thread.sleep(1000)方法来间隔1s来执行代码。这样对应到scheduleAtFixRate这个方法中的时候,Thread.sleep()方法就可以直接拿掉了,直接将sleep方法之后的代码放到Runnable子类的run方法中。

Thread.sleep(10*1000)体现在对scheduleAtFixedRate这个方法中最后两个参数的设置上。如图所示:


2). Lamp类的示例代码

package cn.isoftstone.interview.traffic;import java.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit; /** *  * LampController类用来切换不同线路的交通灯 * 每隔10秒将当前灯变红,并按顺序将下一个方向的灯变绿 * @author XinmingZhang * */public class LampController {      private Lamp currentLamp;           /**       * 初始化第一盏要变绿的红绿灯之后启动新的线程进行线程切换       *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&       * LampController设计成单例模式       * StepI: 私有化构造方法       */      private LampController(){            //指定第一盏亮的交通灯            currentLamp =Lamp.S2N;            currentLamp.light();                       //设置定时器            ScheduledExecutorServicetimer =Executors.newScheduledThreadPool(1);            timer.scheduleAtFixedRate(                        new Runnable(){                              @Override                              public void run() {                                    //每隔10s切换下一盏亮的交通灯                                    currentLamp =currentLamp.blackOut();                              }                        },                        10,                        10,                        TimeUnit.SECONDS);      }           //单例模式 ---- StepII:饿汉式  构建全局访问点      public static final LampController lampController =new LampController();}

2.    测试类MainClass程序编写

1). 流程说明

(1). 总的流程

先初始化交通灯,这时候交通灯的构造方法会启动新的线程进行10s为间隔的交通灯的切换。之后主线程应该立刻进行Road类的12条线路实例的初始化。初始化吗每一条线路实例的时候,会为这个线路实例启动两个新的线程,分别用来为这条线路实例增加车并使得这个线路中的车辆通过这个线路。

(2). 交通灯实例的初始化

[1]. 这里面没有提到交通灯这个类的实例在哪里初始化。注意由于交通灯类Lamp被设计成枚举类型。

[2].枚举类中枚举成员的默认修饰符是publicstatic final,是static的。并且枚举成员变量在枚举类型的内部都是隐式的创建对象。所以一旦某个类调用了或者涉及了Lamp这个类,这个类就加在到内存中,这样里面的12个成员变量就立刻被实例化成线路对象。

2). MainClass类的示例代码

public class MainClass {      public static void main(String[] args) {            //实例化交通灯控制器实例            //在实例化LampController的过程中,会对相应道路的交通灯进行无限循环的切换            //-----启动了新的线程进行切换            LampControllerlampController =LampController.lampController;                       //给出所有的方向            String[]directions =new String[]{                        "S2N", "S2W", "E2W", "E2S",                        "N2S", "N2E", "W2E", "W2N",                        "N2W", "W2S", "S2E", "E2N"            };                                  //实例化Road的过程              //---- 每一条路的new的过程中会查看这条路的交通灯的颜色并穿过马路 ---- 一个新的线程            //---- 每一条路的自动增加车辆 ---- 有一个新的线程            for(int i=0; i< directions.length; i++){                  new Road(directions[i]);            }      }}

3). 运行结果示例


3.    总体分析总结

总体分析总结】这个环节的目的

总结一下这个项目的大体流程,并从这个流程之中如何产生各个类,如何封装。这个环节应该是最开始的需求分析的关键环节,有这个环节+ 分析出来的各个类才能写出程序。

1). 流程模拟

(1). 文字叙述

[1]. 这个过程用文字叙述是这样的:交通灯在不停的进行切换,路上的车辆看到这条路的交通灯之后,发现是绿灯就通过这条路;发现不是,就不通过。

[2]. 写成单线程的伪代码或者文字代码 -----代码基本上分成两块,如下图


第一块主要就是交通灯切换。切换之后,路上的车开始按照规律穿过马路。【单线程

(2). 从给出的单线程代码块提取类的信息

[1]. 提取第一部分的类和对象

{1}. 显式提取

用语言描述出来的对象,文字中的“交通灯”,这样就是非常明显的要提取出来的类,并这句话对应的代码用到了这个类的实例,所以一定要对这个类进行实例化(new)。

{2}. 隐式提取

{2}1. “交通灯切换”说明交通灯一定不是一个,所以交通灯类一次只能实例化出一个实例,所以这个交通等类本身不能直接提供切换的方法所以就要把它委托给别的类的一个实例去做“切换”工作,所以间接抽取出交通灯控制器类

{2}2. 这里面用到了“切换”这个功能,也就是调用一个方法所以一定是先有来自某个类的对象,才能调用这个类对象的方法。由于已经抽象出来了交通灯控制器类,所以这个地方一定要new出来这个交通灯控制器类的实例

{3}. 图示


[2]. 提取第二部分的类和对象

{1}. 显式提取

用语言描述出来的对象,文字中的“路”和“汽车”,这样就是非常明显的要提取出来的类,并这句话对应的代码用到了这个类的实例,所以一定要对这个类进行实例化(new)。

{2}. 隐式提取-----没有

{3}. 图示


【隐式提取】一般从给出的功能中进行提取。如果这个功能(动作)前面的名词不是动作的发出者并且动作的发出者又不明确,那么就一定存在一个隐式的类的实例在操作这个“提到的名词”对应的类的实例。

(3). 细化给出的文字伪代码

[1]. 第一块代码细化

{1}. 图示


{2}. lampController实例中一定是能够直接操作到前面定义12个交通灯实例,所以lampController里面非常有可能直接获取12个交通灯组成的集合。

可能性I:这12个交通灯作为共享数据被直接使用-----枚举成员的特点 publicstatic final  ---- 这不就是全局常量么?

注意枚举成员最方便实现共享数据的办法

可能性II:LampController类直接聚合一个List<Lamp> lamps ----私有数据的思路

可能性III:直接LampController一样直接new出来。如果选择直接new Lamp出来,那么LampController这个切换的功能就直接需要接受一个List<Lamp>来传入这个12盏交通灯,很麻烦。

注意】由于后面Road类也使用到了一条路上对应的Lamp类实例,所以Lamp不能设计成LampController类的聚合私有对象。同时由于Lamp有12盏交通灯,并且是固定不变的,所以设计成共享数据---- 枚举形式更合理,而不是设计成主线程的List<Lamp>再向里面传参数。

[2]. 第二块代码细化

{1}. 图示


{2}. Road实例中一定是能够直接操作到前面定义1000+个车辆实例,所以lampController里面非常有可能直接获取1000+个交通灯组成的集合。

可能性I:枚举类型的共享常量

别的类用不上,并且车辆的数目不固定。所以枚举类型不合适。

可能性II:Road类直接聚合一个List<Car>vehicles ----私有数据的思路

可能性III:直接Road一样直接new出来。如果选择直接new Lamp出来,那么LampController这个增加车辆的功能就直接需要接受一个List<Lamp>来传入这个1000+辆汽车。【很麻烦舍弃

(4). 这个单线程的伪代码

[1]. 整合一下,看看是否可以达到理想的运行效果


[2]. 单线程存在的问题

{1}. 当主线程运行到切换功能,由于是死循环,所以后面的代码没有办法执行到。但是实际上是想让路上的车运行和交通灯切换同时运行,所以上下两部分分成两个线程执行

{2}. 下面的增加车和放行车两个功能又是不能顺序执行,相同时执行,所以封入两个线程。

{3}. 总的线程图如下:


注意】做到这种程度之后,就可以将各个功能嵌入到每个模块进行分析了。

 

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
原创粉丝点击