黑马程序员——交通灯系统

来源:互联网 发布:魔客吧还原数据密码 编辑:程序博客网 时间:2024/06/14 18:35
------- android培训java培训、期待与您交流! ----------

面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法。
再牢牢记住几个案例就可以了:
人在黑板上画圆——>圆心跟半径都是圆本身的属性,所以应该由圆本身提供此方法
列车司机紧急刹车
售货员统计收获小票的金额——>金额是小票上的属性所以此方法该在小票上
你把门关上
小球从绳子的一段移动到绳子的另一端
两块石头磨成石刀,石刀砍树,做成椅子。
(如果将一个对象变为另一个对象的方法一般不会放在原本对象类中,而用工厂模式
StoneKnife = KnifeFactory.createKnife(stone) )
Stone
StoneKnife
tree
chair = ChairFactory.makeChair(material)
material
chair

JAVA1.5的线程池

ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(new Runnable(){public void run(){}});

定时器:就是一种线程控制器。
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){public void run(){}},//需要重复干的事情
1,//过多少秒干第一次
1,//之后隔多少秒
TimeUnit.SECONDS);

枚举的构造方法必须是私有的

 交通灯项目

 

项目需求:

模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:

1、  异步随机生成按照各个路线行驶的车辆。

例如:由南向而来去往北向的车辆-----直行车辆

      由西向而来去往南向的车辆-----右转车辆

      由东向而来去往南向的车辆-----左转车辆

      。。。。

2、  信号灯忽略黄灯,只考虑红灯和绿灯。

3、  应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。

4、  具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。

注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。

5、  每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。

6、  随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。

7、  不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

 

总共有12条路线,为了统一编程模型,可以假设每条路线都有一个红绿灯对其进行控制,右转弯的4条路线的控制灯可以假设为常绿状态,另外,其他的8条线路是两两成对的,可以归为4组,所以,程序只需考虑图中标注了数字号的4条路线的控制灯的切换顺序,这4条路线相反方向的路线的控制灯跟随这4条路线切换,不必额外考虑。

 

 

 

我的分析:


一共12条路线,那么运行的时候就会有12个线程不停得给每天路线上new车
还有12个线程(定时器)每一秒remove掉一个路线上的car。
这样就出现了一个问题,一个线程要调用另一个线程的数组,那么就得static而一旦static的话,
那么每个方向上的数组都将同步,这并不是我们想要的。
我们只想两个线程共享一个数组,老师用了一个绝佳的方法解决了此问题,就是在Road类的构造器
中创建出两个线程,既不影响Road对象的创建,又实现了两个线程共享一个数组,还简化了代码量。

那么每隔一段时间交通灯的灯系统都会变化一次,这个也得由一个单独线程控制。

还有怎么实现两两线程的切换呢?那么就得用一个对象的属性作为开关,也就是控制器所用的对象,
那么remove线程中会有一个判断,如果不能运行那么久让他wait,并且notify其他所有线程。
老师解决的方案就是用一个定时器,那就不需要wait跟notify了,真心漂亮。

如果我这么做的话,我必须得手动写4个类实现runnable接口,那将非常累,代码重复量过多,真心劣质。

 

老师仅仅使用了一个枚举类,跟一个控制类,就实现了5组对象的同时、多种控制,高复用明显。
老师的这个小项目,不管我怎么读,都感觉妙不可言,再无可优化。


 代码编写:

Road类:用于随机new出自己线上的车以及使车通过的remove。

此方法的妙处就是:在构造器内部创建两个线程,即不影响构造器的创建,又实现了两个线程同享一个List,非常妙。

 

package com.isoftstone.interview.traffic;import java.util.ArrayList;import java.util.List;import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class Road {List<String> vechicles = new ArrayList<String>();private String name = null;public Road (String name){this.name = name;// 想实现每条路的不断创建车辆,除了实现runnable接口以外,// 还可以直接在构造方法里面再创建一个线程// 因为必须先有对象才能调用方法,而这个是永动方法,所以定义在外面然后调用// 不如直接用多线程放在构造方法之中。// 其实实现runnable接口只能有一个run方法,只能实现new车跟move车中的一个,// 如果都需要实现只能再新建一个线程了。// 如此看来定义在构造方法中的想法实在是太棒了。ExecutorService pool = Executors.newSingleThreadExecutor();pool.execute(new Runnable(){public void run(){for (int i = 1; i <= 1000; i++) {try {Thread.sleep((new Random().nextInt(10)+1)*1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 内部类访问外部类中的成员变量有两种方法:// 1.将外部类中的成员变量设为final类型// 2.访问的时候格式换为:外部类类名.this.变量名vechicles.add(Road.this.name +"_"+i);}}});// 每隔一秒remove掉一个ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);timer.scheduleAtFixedRate(new Runnable(){public void run(){if (vechicles.size()>0) {// 灯绿了才让行boolean lighted = Lamp.valueOf(Road.this.name).isLighted();if (lighted) {System.out.println(vechicles.remove(0)+"  is  travaling!");}}}},1, 1,TimeUnit.SECONDS);}}

package com.isoftstone.interview.traffic;public enum Lamp {S2N("N2S", "S2W", false), S2W("N2E", "E2W", false), E2W("W2E", "E2S", false), E2S("W2N", "S2N", false), N2S(null, null, false), N2E(null, null, false), W2E(null, null, false), W2N(null, null, false),// 右拐S2E(null, null, true), E2N(null, null, true), N2W(null, null, true), W2S(null, null, true);// 如果这里的参数是一个对象,那么就会出现一个不断需要先new对方的死循环,也就是先有鸡还是先有鸡蛋的问题// 所以该成同名字符串代替private Lamp(String opposite, String next, boolean lighted) {this.opposites = opposite;this.next = next;this.lighted = lighted;}private boolean lighted;private String opposites;private String next;public boolean isLighted() {return lighted;}public void light() {this.lighted = true;// 一组对应的灯就只有一个有对应的灯if (opposites != null) {Lamp.valueOf(opposites).light();}// name()是枚举的内部方法System.out.println(name() + "灯变绿了!");}public Lamp blackOut() {// 将当前灯状态改为红、falsethis.lighted = false;// 将对应方向的灯也改为falseif (opposites != null) {Lamp.valueOf(opposites).blackOut();}// 将下个方向的灯变绿Lamp nextLamp = null;if (next != null) {nextLamp = Lamp.valueOf(next);System.out.println("路灯从" + name() + "切换成——————>" + next + "  "+ opposites);Lamp.valueOf(next).light();}return nextLamp;}}


 

Lamp类跟LampController控制类

实现了对5类对象多个属性的有序控制,非常伟大。

 

package com.isoftstone.interview.traffic;public enum Lamp {S2N("N2S", "S2W", false), S2W("N2E", "E2W", false), E2W("W2E", "E2S", false), E2S("W2N", "S2N", false), N2S(null, null, false), N2E(null, null, false), W2E(null, null, false), W2N(null, null, false),// 右拐S2E(null, null, true), E2N(null, null, true), N2W(null, null, true), W2S(null, null, true);// 如果这里的参数是一个对象,那么就会出现一个不断需要先new对方的死循环,也就是先有鸡还是先有鸡蛋的问题// 所以该成同名字符串代替private Lamp(String opposite, String next, boolean lighted) {this.opposites = opposite;this.next = next;this.lighted = lighted;}private boolean lighted;private String opposites;private String next;public boolean isLighted() {return lighted;}public void light() {this.lighted = true;// 一组对应的灯就只有一个有对应的灯if (opposites != null) {Lamp.valueOf(opposites).light();}// name()是枚举的内部方法System.out.println(name() + "灯变绿了!");}public Lamp blackOut() {// 将当前灯状态改为红、falsethis.lighted = false;// 将对应方向的灯也改为falseif (opposites != null) {Lamp.valueOf(opposites).blackOut();}// 将下个方向的灯变绿Lamp nextLamp = null;if (next != null) {nextLamp = Lamp.valueOf(next);System.out.println("路灯从" + name() + "切换成——————>" + next + "  "+ opposites);Lamp.valueOf(next).light();}return nextLamp;}}


 

package com.isoftstone.interview.traffic;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class LampController {private Lamp currentLamp;public LampController(){currentLamp = Lamp.S2N;currentLamp.light();// 控制器也必须自己实现一个线程。ScheduledExecutorService timer= Executors.newScheduledThreadPool(1);timer.scheduleAtFixedRate(new Runnable(){public void run(){currentLamp = currentLamp.blackOut();}},10, 10, TimeUnit.SECONDS);}}


MainClass入口类

package com.isoftstone.interview.traffic;public class MainClass {public static void main(String[] args) {String[] directions = new String[]{"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"};// 运用数组巧妙的创建了12个方向的路。for (int i = 0; i < directions.length; i++) {new Road(directions[i]);}// 打开控制器new LampController();}}


 

 

 

------- android培训java培训、期待与您交流! ----------
原创粉丝点击