交通管理灯项目

 

 

交通管理灯项目模拟了公路上十字路口红绿灯的控制系统,由于车辆靠右行驶,有四条右转向路线是不受交通灯控制的,任何时候都可以通行。分别是“南到东”S2E、“东到北”E2N、“北到西”N2W、“西到南”W2S。。

需要考虑的是直行行驶和左转向行驶

直行行驶

1.正南(N2S)行驶和正北(S2N)行驶为同一交通灯状态

2.正东(W2E)行驶和正西(E2W)行驶为同一交通灯状态

左转向行驶。

3.西到北(W2N)和东到南(E2S)为同一交通灯状态。

4.南到西(S2W)和北到东(N2E)为同一交通灯状态。

 

总共有四种路线由异步生成

考虑到生活中先直行后转向

所以依次按序号1正南正北。

其次4南到西,北到东。

再次2正东正西。

最后3西到北和东到南

 

如图

根据图示有一个清晰的思路

一、   每条路线上都会出现很多车辆,由随机生成器生成

在这条路上如果是绿灯,则每秒减少一个车辆

1.    设计一个Road类来表示路线,每一个Road对象代表有一条路线。由于总共12条路线。所以生成12个Road对象。

2.    每条路线上随机增加车辆,把增加的车辆存储到一个集合中。

3.    每条路线每秒检查自己是否为绿,如果为绿则把本集合中第一个存储的对象移除。。

二.每条路线每秒都检查自己的路线上的灯是否为绿,如果为绿移除集合中第一个车辆,并将下一条路线上的灯变红。

1.每个交通灯要有自己变绿或变黑的方法。并且返回自己的亮灯的状态。设计一个Lamp类来表示交通灯。里面有变绿和变黑的方法。、

2.由于有12条路线所以需要产生12个交通管理灯。右转向的4个灯不受控制应该为常亮。不能被改变,或者不设置,本系统设置为常亮状态。

3.上面提到剩余的8种路线是相对的,所以其路线上对应的灯,也应该是成对变亮或者变黑。所以在Lamp的方法中当自己的灯变亮或者变黑时,也让对应的灯变亮或者变黑。

4.运用枚举。因为无论程序的哪里用到交通灯时都是指同一个对象,随意用枚举方法有更大的便捷性。

三、设计一个灯的控制管理器LampControl类,让灯定时变亮或变黑。

面向对象设计把握一个重要的概念:谁拥有数据,谁就对外提供操作这些数据的方法。。这被称为专家模式。。

 

此项目总共设计了四个类

分别是Road、Lamp、LampController、MainClass

Road类

1.每个对象都有一个name成员变量来代表方向。Vehicles成员变量来表示车辆的集合

2.在Road类对象的构造方法中启动一个线程。然后每隔一段时间后集合vehicles中添加一辆车。用路线名_id来表示

3.在Road对象中启动一个定时器,每隔一秒检查路上的灯是什么状态,是亮的话打印当期路线上的车辆数,并且每秒去移除集合中的一辆车。

Executor是一个线程工具类,里面有线程调度池

ScheduledExecutorService timer= Executors.newScheduledThreadPool(1);

然后设置定时器 timer.scheduleAtFixedRate();

 

 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 {


/**
* @param args
*/
//定义一个车属性代表路上通行的车,车属性为一个集合代表路上车的数量
List<String> vechicles = new ArrayList<String>();

//定义路的名字,这里的名字主要是指路上车的行车方向
private String name = null;


//内部类访问外部类
//public Road(finall String name){   细节也可以用Raod.this.name
//定义类Road类的构造器,传入一个name属性,并初始化
public Road(String name){
this.name = name;

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) {
e.printStackTrace();
}
vechicles.add(Road.this.name+"_"+i); 
}
}

});

//定义一个延迟执行的线程池timer,  //模拟当绿灯亮时每隔一段时间就让一辆车,这里是的时间间隔是1秒
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){
public void run() {
if(vechicles.size()>0){
//boolean lighted = true;
boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
//if(lighted==true)
if(lighted)
{
//remove返回值是什么  正在取得的那个值
System.out.println(vechicles.remove(0) + "is traversing !");
}
}
}
}, 
1, //initialDelay
1, //period
TimeUnit.SECONDS);//unit
/*
timer.schedule(
new Runnable(){
public void run() {

}},
1,//过多久去干这件事
unit);*/

}

}
 

Lamp类

定义成枚举类型。生成12种灯。并将分分成提供一个构造参数private Lamp(String opposite, String next, boolean lighted){

       this.opposite =opposite;

       this.next = next;

       this.lighted =lighted;

  }

S2N("N2S","S2W",false)

提供变亮灯的方法。Light()

灯变暗的方法blackOut()     变红时,对应方向的灯也要变红,并且下一个方向的灯要变绿。并返回下一个灯

设置私有变量 boolean  lighte、String  opposite、String  next;

每个Lamp对象中的亮黑状态用lighted变量来表示,选用S2N、S2W、E2W、E2N这四个方向上的Lamp对象依次轮询变亮,

Lamp对象中还要有一个oppositeLampName变量来表示他们相反方向上的灯。再用一个nextLampName变量来表示他们相反方向上的灯。这三个变量用构造方法来赋值。因为枚举元素在定以后无法在构造方法中彼此去引用,所以,相反方向和下一个方向用字符串去表示。

除了S2N,S2W,E2W,E2S,这设置构造参数,其他方向上的灯的nextLampName和oppositeLampName属性设置为空。防止彼此之间互相调用light和blackOut然后进入死循环

 

package com.isoftstone.interview.traffic;


/*S2N,S2W,E2W,E2S,
N2S,N2E,W2E,W2N,
S2E,E2N,N2W,W2S
*/
public enum Lamp {
//N2S先定义后使用  S2N(N2S) 该成S2N(“N2S”)..直接传灯传不过去
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);


//3个构造参数,1。对应的灯。。2.下一个灯。。3.灯开始的参数。。
private Lamp(String opposite, String next, boolean lighted){
this.opposite = opposite;
this.next = next;
this.lighted = lighted;
}

//因为有的灯没有构造方法,所以补一个空的构造方法
private Lamp(){

}

private boolean lighted;
//private Lamp opposite;
private String opposite;
private String next;

public boolean isLighted(){
return lighted;
}

/**
* 等变绿时,它对应方向的灯也要变绿
*/
public void light(){
this.lighted = true;
if(opposite != null){
Lamp lampOpposite = Lamp.valueOf(opposite);
lampOpposite.light();
//Lamp.valueOf(opposite).light();
}
System.out.println(name() + " lamp is green, 下面总共应该有6个方向能看到汽车穿过!");
}

/**
* 灯变红时,对应方向的灯也要变红,并且下一个方向的灯要变绿
* 把当前灯变红,把下一个灯变绿,并返回下一个灯。。
* @return 下一个要绿的灯
*/

public Lamp blackOut(){
this.lighted = false;
if(opposite != null)
Lamp.valueOf(opposite).blackOut();

/**
* 严重提示。。。。。。。。。
*/
Lamp nextLamp = null;
//Lamp nextLamp = Lamp.valueOf(next);
if(next != null){
nextLamp = Lamp.valueOf(next);
nextLamp.light();
System.out.println("绿灯从" + name() + "-------切换为" + next);
}
return nextLamp;
}
}

 

 

LampController类

1.  交通管理等的控制系统类,此类对好设计成单例模式

2.  、设置第一个灯为绿灯,此类对象用start方法启动,并运用定时器每隔十秒将下一路线上的灯变亮

 

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(){
System.out.println("lai a >>>>>>>>>>>>>>>>>");
currentLamp = currentLamp.blackOut();
}
},
8,//initialDelay
8,//period
TimeUnit.SECONDS);//unit
}

}

 

MainClass类

用for循环生成12条路线,然后new一个控制LampController();启动运行

 

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"
};

for(int i=0;i<directions.length;i++){
new Road(directions[i]);
}
new LampController();
}
}

 

听了张老师的一个小小的项目讲了很多很多内容,第一遍不是太清楚,第二遍感觉妙不可言。定时器这个不怎么熟悉,以及后面的银行调度系统都运用了定时器。以及面向对象的思想。谁拥有数据,谁就对外提供操作数据的方法。。。比如关门,关门这个应该封装到门的类里面,人只是给了一个动作,而门自己有关的方法,当门碰到障碍物的时候就停了下来,类似的火车的刹车系统也应该是火车的。。还有应该熟悉单例模型,这个一定要掌握。。项目写完,又看了一些牛人对项目的分析更加有了深刻的了解,感觉自己理解的比较浅,别人进行了深刻的剖析,自己也应该对自己写过的学过有一个非常非常清晰的剖析,能够讲出来写出来。。加油!

 

 

附:

下面是所用到的API解释

/*
java.util.concurrent 
类 Executors


public class Executors
extends Object此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。此类支持以下各种方法: 


创建并返回设置有常用配置字符串的 ExecutorService 的方法。 
创建并返回设置有常用配置字符串的 ScheduledExecutorService 的方法。 
创建并返回“包装的”ExecutorService 方法,它通过使特定于实现的方法不可访问来禁用重新配置。 
创建并返回 ThreadFactory 的方法,它可将新创建的线程设置为已知的状态。 
创建并返回非闭包形式的 Callable 的方法,这样可将其用于需要 Callable 的执行方法中。 


返回类型  static ScheduledExecutorService 
newScheduledThreadPool(int corePoolSize) 
          创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 
          
返回类型    static ExecutorService 
newSingleThreadExecutor() 
          创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 


返回类型  static ExecutorService 
newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 
          创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程。 
          
java.util.concurrent 
接口 ExecutorService


public interface ExecutorService
extends ExecutorExecutor 提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 


可以关闭 ExecutorService,这将导致其停止接受新任务。关闭后,执行程序将最后终止,这时没有任务在执行,
也没有任务在等待执行,并且无法提交新任务。 


通过创建并返回一个可用于取消执行和/或等待完成的 Future,
方法 submit 扩展了基本方法 Executor.execute(java.lang.Runnable)。方法 invokeAny 和 invokeAll
 是批量执行的最常用形式,它们执行任务集合,然后等待至少一个,或全部任务完成(可使用 ExecutorCompletionService 
 类来编写这些方法的自定义变体)。 


Executors 类提供了用于此包中所提供的执行程序服务的工厂方法。 




public interface ScheduledExecutorService
extends ExecutorService一个 ExecutorService,可安排在给定的延迟后运行或定期执行的命令。 


schedule 方法使用各种延迟创建任务,并返回一个可用于取消或检查执行的任务对象。
scheduleAtFixedRate 和 scheduleWithFixedDelay 方法创建并执行某些在取消前一直定期运行的任务。 


用 Executor.execute(java.lang.Runnable) 和 ExecutorService 的 submit 
方法所提交的命令,通过所请求的 0 延迟进行安排。schedule 方法中允许出现 0 和负数延迟(但不是周期),
并将这些视为一种立即执行的请求。 


所有的 schedule 方法都接受相对 延迟和周期作为参数,而不是绝对的时间或日期。
将以 Date 所表示的绝对时间转换成要求的形式很容易。例如,要安排在某个以后的日期运行,
可以使用:schedule(task, date.getTime() - System.currentTimeMillis(),
 TimeUnit.MILLISECONDS)。但是要注意,由于网络时间同步协议、时钟漂移或其他因素的存在,
 因此相对延迟的期满日期不必与启用任务的当前 Date 相符。
Executors 类为此包中所提供的 ScheduledExecutorService 实现提供了便捷的工厂方法。 
*/ 

------- android培训java培训、期待与您交流! ----------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值