交通灯管理系统

0. OO原则


谁有数据,谁有方法。

最典型的问题:人在黑板上画圆。其中涉及3个类,人-Person类,黑板-BlackBoard类,圆-Circle类。画圆动作到底是属于谁?应用上面原则,发现圆最重要的两个数据,一个是圆心,另一个是半径,它们同属于圆这个类,而画圆就是基于这两个数据来进行的,所以画圆应该是圆的方法。这与我们最本质的认识是有区别的,因为明明是人画的圆啊。此处可以这样理解,在面向对象中最重要的是每个单独的类,另一个非常重要的是各个类之间的关系,它们之间存在着相互的调用关系,人调用了画圆的方法。


1. 项目需求


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

1)异步随机生成按照各个路线行驶的车辆(多个线程控制)。
例如:
由南向而来去往北向的车辆 —- 直行车辆
由西向而来去往南向的车辆 —- 右转车辆
由东向而来去往南向的车辆 —- 左转车辆
。。。
2)信号灯忽略黄灯,只考虑红灯和绿灯。
3)应考虑左转车辆控制信号灯,右转车辆不受信号灯控制(抽象为常绿的灯)。
4)具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:

1)南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
2)每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
3)随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
4)不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。


2. 系统分析


根据项目需求可以画出下面示意图(越是复杂的东西越要学会用图表示,直观)( PS:为了更好地完成此项目,连续两天晚上都回去我们学校旁边的岱宗大街+康复路与龙潭路十字路口观察,现在对此已十分了解


由上图可以看出系统有12条路线(N2W、E2N、S2E、W2S...)组成,每条路线对应一个交通灯,每个灯有红、绿(忽略黄色),当灯为绿时,该灯对应的路线上可以有汽车通过。

3.系统设计

通过上方的分析可以看出,其实有三个类在工作,分别道路Road类、交通灯Lamp类、交通工具Vehicle类(此处省略此类,用字符串代替)。
当把上面12条路线抽象出一条路线,整个系统的架构以及各个模块之间的关系应该是如下图所示:


4.详细设计及编码


其中注释就是详细设计的过程。

1)Lamp类

package tad.blog.traffic;

/**
 * 交通灯类
 * 
 * 因为灯的个数是确定,所以应用枚举解决。
 * 每条路线应该有一个灯控制,共12条路线,所以应该有12个灯,即12个枚举元素。
 * 因为每次同时进行变化的都是逆方向变化,所以可以用一个灯来代表一组灯简化系统设计。
 * 分组如下,只需要控制每组灯中的一个即可。
 * S2N,N2S
 * S2W,N2E
 * E2W,W2E
 * E2S,W2N
 * 因为右转不受控制,所以修定绿灯常量简化问题。即从南向东和从西向北及对应方向不受红绿灯的控制,会做特殊处理。
 * S2E,N2W
 * E2N,W2S
 * 
 * @author tad http://weibo.com/weinabanta
 */
public enum Lamp {
	//每个元素表示一个交通灯
    //项目要求右拐路线不受红灯控制,可假想总是绿灯来简化问题
	S2E(true,null,null),N2W(true,null,null),E2N(true,null,null),W2S(true,null,null),
    //最主要的四个灯,其它灯的变化都是围绕此展开。
    S2N(false,"N2S","S2W"),S2W(false,"N2E","E2W"),E2W(false,"W2E","E2S"),E2S(false,"W2N","S2N"),
    //下面的灯方向与上面对应灯相反,因为灯的变化一样,所以“相反方向灯”和“下一个灯”设为null,但不影响使用,因为通过上面4个灯可以把所有灯都联系起来。
    N2S(false,null,null),N2E(false,null,null),W2E(false,null,null),W2N(false,null,null);
    //因只考虑红、绿两种状态,故可用boolean类型表示灯的状态:true为绿灯,false为红灯。
    private boolean status;
    //与当前灯同时为绿的对应方向。
    private String opposite;
    //当前灯变红时下一变绿的灯。
    private String next;
    //构造器
    private Lamp(boolean status,String opposite,String next){
        this.status = status;
        this.opposite = opposite;
        this.next = next;
    }  
    //返回当前灯的状态
    public boolean getStatus(){
        return status;
    } 
    //灯变绿
    public void toGreen(){
        this.status = true;
        System.out.println(name() + " 变绿,总共会有6个方向汽车穿过...");
        if(opposite != null){
            Lamp.valueOf(opposite).toGreen();
        }
    }
    //灯变红
    public void toRed(){
        this.status = false;
        if(opposite != null){
            Lamp.valueOf(opposite).toRed();
        }
    }   
    //下一变绿的灯
    public Lamp next(){
        if(next != null){
            return Lamp.valueOf(next);
        }
        return null;
    }
}

2)Road类

package tad.blog.traffic;

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;

/**
 * 每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。
 * 每条路线上随机增加新的车辆,增加到一个集合中保存。
 * 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
 * @author tad http://weibo.com/weinabanta
 */
public class Road {
	//路名字
    private String name;
	//路上的车,不必设为private
    private ArrayList<String> vehicles = new ArrayList<String>();
    //构造器
    public Road(String name) {
        this.name = name;
        //使用线程池中线程模拟有车不断上路,随机的。
        ExecutorService pool = Executors.newSingleThreadExecutor();
        pool.execute(new Runnable(){
            public void run() {
                for(int i = 0;i< 9999;i++){
                    try {
                        Thread.sleep((new Random().nextInt(10) + 1) * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //访问外部类的字段
                    vehicles.add(Road.this.name + "_" + i);
                }
            }
 
        });
 
        //使用ScheduledExecutorService线程池中的线程每隔一秒检查相应的灯是否为绿,如果是则放行一辆车
        ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
        timer.scheduleAtFixedRate(
                new Runnable(){
                    public void run() {
                        if(vehicles.size()>0){
                            boolean status = Lamp.valueOf(Road.this.name).getStatus();
                            if(status){
                            	//移除顶上的车
                                System.out.println(vehicles.remove(0) + " is traversing !");
                            }
                        }
                    }
                },
                1,
                1,
                TimeUnit.SECONDS);
    }
}

3)LampController类

整个系统的控制中心,掌握所有灯的变化。
package tad.blog.traffic;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 因为LampController在整个系统中只能有一个,使用设计模式中的单例模式,还是饿汉模式。
 * @author tad http://weibo.com/weinabanta
 */
public class LampController {
    private Lamp currentLamp;
    private static LampController controller = new LampController();
    //当前灯
    private LampController(){
        this.currentLamp = Lamp.S2N;
    }
    //返回实例
    public static LampController getInstance(){
        return controller;
    }
    //开始运行此交通灯系统
    public void start(){
        //将当前灯变绿
        currentLamp.toGreen();
        //再次使用到ScheduledExecutorService线程池,启动定时器,每隔10秒将当前灯变红,并将下一个灯变绿,也正因为此所有灯都变化起来了。
        ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
        timer.scheduleAtFixedRate(
                new Runnable(){
                    public void run() {
                    	//完成灯的切换
                        System.out.println("绿灯从 " + currentLamp.name() + " --------> 切换为 " + currentLamp.next().name());
                        currentLamp.toRed();
                        currentLamp = currentLamp.next();
                        currentLamp.toGreen();
                    }
                },
                10,
                10,
                TimeUnit.SECONDS);
    }
 
}

4)Boss类

把所有类整合到一起,系统完成。
package tad.blog.traffic;

/**
 * 把所有类整合起来
 * @author tad http://weibo.com/weinabanta
 *
 */
public class Boss {
    public static void main(String[] args) {
        //产生12个方向的路线
        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]);
        }
        //得交通灯控制器,调用方法使整个系统动起来。
        LampController.getInstance().start();
    }
}

5.项目总结


1)真正做好一个项目是需要建立对业务流程的熟练、准确掌握,这是最根本的,此处只有真正了解了红绿的调度过程才能真正做此系统,这也是我为什么连续两天去十字路口观察的原因。
2)面向对象,包括OOA、OOD、OOP需要大量练习才能真正掌握,而这也是成为一个优秀程序员的根本。此项目其实并不是完全面向对象的,比如开走车的方法不应该放在Road中。
3)设计模式一定要掌握,可以解决太多问题,比如此项目用了单例模式,保证了整个项目只有一个LampController。
4)对Java语法及核心类库要熟练掌握。此处用到多线程、枚举、内部类等相对高级的技术。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值