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

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

项目需求

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

1.异步随机生成按照各个路线行驶的车辆。例如:由南向北的直行车辆,由西向南的右转车辆,由东向南的左转车辆。

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

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

4.具体信号灯控制逻辑与现实生活逻辑相同,例如:南北向车辆与东西向车辆交替放行,通方向等待车辆应先放行直行车辆而后放行左转车辆

5.每辆车通过路口的时间为1秒

6.随机生成车辆时间间隔和红绿灯切换时间间隔可以设置

7.不要求实现GUI,只考虑系统逻辑实现


黑马程序员——交通灯管理系统_第1张图片

由上图分析可知,十字路口应受交通灯影响的汽车行驶车道(方向)为1、2、4、5、7、8、10、11共8个车道,就应该有对应的8个方向的信号灯。信号灯变绿的顺序是2和8,然后是1和7,然后是5和11,然后是10和4。

面向对象设计

面向对象设计(OOD)就是根据项目需求进行面向对象分析(OOA),然后把项目中有哪些对象,对象中有哪些方法(行为)和字段(数据)搞清楚。面向对象设计要比面向对象编程(OOP)重要许多,面向对象设计做好了,面向对象编程只是照着设计图去完善细节。所有的设计模式(design pattern)都是面向对象设计经验。

OOD经验:谁拥有数据,谁就对外提供操作这些数据的方法。 

例如:人在黑板上画圆,画圆的方法应该在圆对象中。

例如:列车司机紧急刹车,刹车的方法应该在列车对象中。

例如:售票员统计售货小票的金额,统计金额的方法应该在小票对象中。

例如:你把门关上了,关门的方法应该在门对象中。

例如:球从一根绳子的一端移动到了另一端,对象有两个球和绳子,移动应该是球对象的方法,移动的是位置,所以球对象应该还有一个位置属性,绳子对象应该有起点和终点属性,还应该有一个提供球移动下一位置的方法:代码如下:

class Rope{
	Point start;
	Point end;
	public Rope(Point start,Point end) {
		this.start=start;
		this.end=end;
	}
	public Point nextPoint(Point currentPoint){
		/*通过两点一线的数学公式可以计算出当前点的下一个点,
		 * 这个细节不属于设计阶段要考虑的问题
		 * 如果当前点是终止点,则返回null,
		 * 如果当前点不是线上的点,则抛出异常*/
	}
}
class Ball{
	Rope rope;
	Point currentPoint;//当前位置
	public Ball(Rope rope,Point startPoint){
		this.rope=rope;
		this.currentPoint=startPoint;
	}
	public void move(){
		currentPoint=rope.nextPoint(currentPoint);
		System.out.println("小球移动到了"+currentPoint);
	}
}
例如:两块石头磨成一把石刀,石刀可以砍树,砍成木材,木材做成椅子。

StoneKnife=KnifeFactory.createKnife(stone);

Timber=StoneKnife.cut(Tree)

Chair=ChairFactory.makeChair(Timber);

详细设计

来分析一下交通灯系统:初步设想一下都有哪些对象:红绿灯,车,车道。由于有8个红绿灯需要同步转换红绿,所以需要一个红绿灯控制系统。又由于本项目不要求体现车辆的整个移动过程,只是捕捉车辆穿过路口的过程,所以车不需要单独设计为一个对象,应该作为车道的一个集合属性。这样最终就有三个对象:红绿灯,红绿灯控制系统,车道。

首先分析红绿灯对象,一共有8个红绿灯,把右转弯方向也考虑成一个常绿的红绿灯,则一共有12个红绿灯,由于每个红绿灯控制的方向恒定保持不变,所以考虑用枚举实现红绿灯,里边包含12个枚举值。每个红绿灯枚举值包含三个属性,第一个是当前灯的颜色,第二个是对面灯的枚举值,第三个是下一个要亮的灯的枚举值。

编写Road类

根据上面的分析,应该有一个车道类起名为Road:
import java.util.ArrayList;
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 {
	//道路上的车辆
	private ArrayList<String> automobiles=new ArrayList<String>();
	private String name=null;//路的名字
	//在构造函数中使路‘活’起来,既车辆随机进入此车道,并根据本车道的交通灯通过路口
	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 {
						//本线程随机休息1到10秒
						Thread.sleep((new Random().nextInt(10)+1)*1000);
					} catch (InterruptedException e) {}
					automobiles.add(Road.this.name+"车道的第"+i+"辆车");
					System.out.println(Road.this.name+"车道的第"+i+"辆车进入"+Road.this.name+"车道");
				}
			}
		});
	    //如果一辆车看见自己的灯是绿的,则车通过
	    ScheduledExecutorService timer=Executors.newScheduledThreadPool(1);
	    timer.scheduleAtFixedRate(new Runnable() {
			public void run() {
				if (automobiles.size()>0) {
					boolean lampState=true;
					if (lampState==true) {
						System.out.println(automobiles.remove(0)+"离开"+Road.this.name+"车道");
					}
				}			
			}
		}, 1, 1,TimeUnit.SECONDS);	    
	}
}


编写Lamp枚举

之所以要用枚举来表示灯对象,因为要勇于尝试新技术。张孝祥张老师的视频中用N2S这样表示方向,我这里用汽车来的方向+车道号来表示方向,例如:N2S就写成N8,见项目需求图。并优化变量名和方法名。代码如下:
public enum Lamp {
	S1(false,"N7","E5"),S2(false,"N8","S1"),S3(true,null,null),
	E4(false,"W10","S8"),E5(false,"W11","E4"),E6(true,null,null),
	N7(false,"S1","W11"),N8(false,"S2","N7"),N9(true,null,null),
	W10(false,"E4","S2"),W11(false,"E5","W10"),W12(true,null,null);
	//表示当前交通的状态,是否是绿的,true为绿
	private boolean isGreen;
	//表示相反方向交通灯的名字
	private String opposite;
	//表示下一个要亮起的交通灯的名字
	private String next;
	//构造方法初始化各个成员变量
	Lamp(boolean isGreen,String opposite,String next){
		this.isGreen=isGreen;
		this.opposite=opposite;
		this.next=next;
	}
	//获取当前交通灯状态
	public boolean getIsGreen(){
		return isGreen;
	}
	//使当前交通灯变绿
	public void turnGreen(){
		//将当前交通灯变绿
		this.isGreen=true;
		//将对面交通灯也变绿
		Lamp oppo=Lamp.valueOf(opposite);
		if (oppo.isGreen==false) {
			oppo.turnGreen();
		}
	}
	//使当前交通灯变红
	public void turnRed(){
		//将当前交通变红
		this.isGreen=false;
		//将对面交通灯也变红
		Lamp oppo=Lamp.valueOf(opposite);
		if (oppo.isGreen==true) {
			oppo.turnGreen();
		}
		//将下一个交通灯变绿
		Lamp nextLight=Lamp.valueOf(next);
		if (nextLight.isGreen==false) {
			nextLight.turnGreen();
		}
	}
}
然后修改Road类中的获取当前车道交通灯状态的代码:
//					boolean lampState=true;
					boolean lampState=Lamp.valueOf(Road.this.name).getIsGreen();

编写LampController类

LampController类主要负责启动交通灯系统,并初始化第一组交通灯状态,代码如下:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class LampController {
	private Lamp currentLamp;
	public LampController(Lamp inialLamp){
		currentLamp=inialLamp;
	}
	public void start(){
		currentLamp.turnGreen();
		ScheduledExecutorService timer= Executors.newScheduledThreadPool(1);
		timer.scheduleAtFixedRate(new Runnable() {
			public void run() {
				Lamp next= currentLamp.turnRed();
				currentLamp=next;
			}
		},10,10,TimeUnit.SECONDS);
	}
}


编写测试类并运行

测试类主要是测试程序运行结果,先创建12个车道,并使车道通车,然后输出红绿灯的变化对车流的影响:

public class TrafficeDemo {
	public static void main(String[] args) {
		String[] roads=new String[]{
			"S1","S2","S3",
			"E4","E5","E6",
			"N7","N8","N9",
			"W10","W11","W12"
		};
		for (int i = 0; i < roads.length; i++) {
			new Road(roads[i]);
		}
		LampController controller=new LampController(Lamp.S2);
		controller.start();
	}
}
然后修改Lamp枚举的turnRed方法,使它可以返回下一个交通的Lamp对象
	//使当前交通灯变红
	public Lamp turnRed(){
		//将当前交通变红
		this.isGreen=false;
		//将对面交通灯也变红
		Lamp oppo=Lamp.valueOf(opposite);
		if (oppo.isGreen==true) {
			oppo.turnRed();
		}
		//将下一个交通灯变绿
		Lamp nextLight=Lamp.valueOf(next);
		if (nextLight.isGreen==false) {
			nextLight.turnGreen();
		}
		return nextLight;
	}
然后启动程序观察运行结果:
S2的交通灯变绿了
N8的交通灯变绿了
S3车道的第1辆车进入S3车道
N8车道的第1辆车进入N8车道
S3车道的第1辆车离开S3车道
N8车道的第1辆车离开N8车道
W10车道的第1辆车进入W10车道
E4车道的第1辆车进入E4车道
N7车道的第1辆车进入N7车道
W12车道的第1辆车进入W12车道
W12车道的第1辆车离开W12车道
N7车道的第2辆车进入N7车道
E6车道的第1辆车进入E6车道
E6车道的第1辆车离开E6车道
S1车道的第1辆车进入S1车道
N9车道的第1辆车进入N9车道
N9车道的第1辆车离开N9车道
S3车道的第2辆车进入S3车道
S2车道的第1辆车进入S2车道
S3车道的第2辆车离开S3车道
N8车道的第2辆车进入N8车道
N8车道的第2辆车离开N8车道
S2车道的第1辆车离开S2车道
E5车道的第1辆车进入E5车道
S1车道的第2辆车进入S1车道
W11车道的第1辆车进入W11车道
W12车道的第2辆车进入W12车道
S3车道的第3辆车进入S3车道
S3车道的第3辆车离开S3车道
E6车道的第2辆车进入E6车道
W11车道的第2辆车进入W11车道
W12车道的第2辆车离开W12车道
E6车道的第2辆车离开E6车道
N7的交通灯变绿了
S1的交通灯变绿了
S1车道的第1辆车离开S1车道
N7车道的第1辆车离开N7车道
N7车道的第3辆车进入N7车道
S1车道的第2辆车离开S1车道
W10车道的第2辆车进入W10车道
N7车道的第2辆车离开N7车道
E4车道的第2辆车进入E4车道
N7车道的第3辆车离开N7车道
W11车道的第3辆车进入W11车道
每个车道的车辆数单独计算,这样可以回避多线程安全的问题,交通灯依次变绿,右转弯车辆不受交通灯影响。如果改为整个十字路口计算车辆总数和车的次序,则会出现同一辆车进入两个车道的问题。








你可能感兴趣的:(黑马程序员——交通灯管理系统)