黑马程序员_7K面试题_交通灯管理系统

时间:2023-02-18 09:43:24
一,面向对象在分析

        面向对象的一个重要经验:谁拥有数据,谁就对外提供操作这些数据的方法。
比如:人在黑板上画圆,其实并不是人在画,而是黑板在画圆,人调用了黑板的画圆的方法, 其实真正在画圆的是黑板,黑板拥有圆对象,所以黑板应该提供画的方法。 人关门也是一样,我们只是调用了门的关的方法,其实还是门自己关上的。 包括列车司机刹车,售票员售票等等。

1,面向对象案例一:
小球在绳子上移动,面向对象分析。
小球自己拥有一个移动方法。绳子是小球的载体,为小球指引方向,绳子有一个起始点的坐标
然后拥有获取下一个点的坐标方法,小球在初始化时将绳子做为参数传递给它,这样小球就知道了
该往那个方向移动。

class Rope
{
    private Point start;
    private Point end;
    public Rope(Point start,Point end) {
        this.start = start;
        this.end = end;
    }
    //获取下一个点的方法
    public Point nextPoint(Point currentPoint) {
    }
}
class Ball
{
    private Rope rope;
    private Point currentPoint;
    public Ball(Rope rope,Point currentPoint) {
        this.rope = rope;
        this.currentPoint = currentPoint;
    }
    //小球的移动方法
    public void move() {
        currentPoint = rope.nextPoint(currentPoint);
    }
}

2,面向对象案例二:
两块石头磨成一把刀,刀可以砍树砍成木材,木材做成椅子。面向对象解释。
这里的对象:Stone,StoneKnife,Tree,Wood,Chair
石头变成刀,不可能是石头有把自己变成刀的方法,这样的话,石头自己就没有了,石头不可能把自己变没了,所以
应该是有一个生产刀的工厂:KnifeFactory把石头做为材料,拥有生产的方法,返回刀。

class KnifeFactory {
    private Stone stone;
    public KnifeFactory(Stone stone) {
        this.stone = stone;
    }
    
    //生产刀的方法
    public Knife creatKnife(Stone first,Stone second) {
        ....
        return kinfe;
    }
}

同样的道理,将木材变成椅子,一定有一个生产椅子的工厂,接受木材,这个工厂有生产椅子的方法, 返回椅子。
 
二,7K面试题之交通灯管理系统

交通灯管理系统

 

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

 

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

例如:

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

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

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

       。。。

 

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

 

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

 

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

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

 

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

 

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

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

以面向对象的思维初步分析这道题:
        直接从题目表面上看起来,应该要用到的对象有:
红绿灯,红绿灯的控制系统,汽车,路线。
汽车是否穿过路口,要看前面是否有车,应该问谁前面是不是有车呢?是路,路中存储着车辆的集合,所以路上应该有增加和减少车辆的方法。所以只要捕捉出车辆穿过路口的过程,也就是捕捉路上减少一辆车的过程,而车不需要单独作为一个对象,用一个字符串表示即可。 这样以来,正真要我们定义的类应该就只有:交通灯,交通灯控制器,路。


首先,画出路线图:

黑马程序员_7K面试题_交通灯管理系统

1,首先定义一个Road类,每个Road对象代表一条路线,一共有12条路线,所以系统中要创建12个Road的实例对象。

每条路线上随机增加车子,车子用一个字符串代替,将车子存储到集合中。每条路线每隔1秒检查路灯是不是为绿,如果集合中有车子,则从中移除一辆车子。详细过程见注释。


package com.yue.lamp;
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;
/*
 * 每条路线上都会有来来往往的车子,使用面向对象的原理,车子在路口等待和行走,其实就是路线在增加和删除车子,
 * 通过集合存储过来的车子。异步随机生成各个路线的来往车辆,可以使用jdk1.5里面的线程机制,通过创建单一线程池
 * 模拟车子不断增加过程。通过创建调度线程池,模拟车子通过路口。详细分析见注释。
 */
public class Road {
    //创建交通工具集合,存储本路线过来的车子
    private List<String> vehicles/*交通工具*/ = new ArrayList<>();
    //使用字符串来标记车子,向集合中增加车子就是增加一个字符串
    private String name = "";
    public Road(String name) {
        this.name = name;
        /*模拟路线上增加车子的情况
         * 创建线程池,向线程池中添加随机时间增加车子的任务,并由线程池中的一个线程执行该任务.
         */
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.execute(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<1000;i++) {
                    try {
                        //每隔1到10秒钟(改时间随机生成),增加一辆车子,一共增加1000辆车子
                        Thread.sleep((new Random().nextInt(10) + 1) * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    vehicles.add(Road.this.name + "路线上的车子" + i);
                }
            }
        });
        
        /*
         * 模拟车子通过路口,当该路线的交通灯变绿则让车子通过。
         * 题目要求每辆车穿过路口的时间是1秒钟,可以通过一个定时器,每隔1秒钟检查一下交通灯,
         * 如果为绿则从集合中减少一辆车子。
         */
         ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
         timer.scheduleAtFixedRate(
                 new Runnable() {
                    
                    @Override
                    public void run() {
                        //如果路口有车子在等待,判断灯是不是为绿
                        if(vehicles.size() > 0) {
                            boolean lighte = Lamp.valueOf(Road.this.name).islighte();
                            if(lighte) {
                                //交通灯变绿时每隔一秒移除路口的第一辆车
                                System.out.println(vehicles.remove(0) + "通过路口");
                            }
                        }
                    }
                },
                 1,
                 1,
                 TimeUnit.SECONDS);
    }
}

2,定义交通灯类:
每条路线每隔1秒就会检查路灯是不是变绿,当一个灯变红则伴随下一个灯变绿。我们把灯的状态定义成亮和不亮两种状态,分别代表绿灯和红灯。一共12条路线,所以有12个灯,但是右拐的车是不受红绿灯控制的,所以为了程序统一,所以右拐的灯为常亮。除了友拐的灯,其余八个灯是两两成对出现的,可以看成是只有4组灯,只需要取出四组灯中的任意一个,对这四个灯依次顺序变亮,则其余的灯也跟着变化,这时候Lamp对象就需要一个变量来记录自己的相反方向的灯,当自己变黑后,要伴随自己下一个灯变黑,并且返回下一个灯,所以Lamp类在构造方法中应该有自己的下一个灯,相反方向的灯,和自己的状态,三个参数。在获取灯时,都是获取的某个方向的灯的示例对象,所以将灯定义成枚举更方便。

package com.yue.lamp;
/*一共有12条路线,必须要有12个交通灯,来控制车辆是不是通行。因为右转的车子是没有限制的,我们也给这四条路线各定义一个
灯,并且该灯是常绿的。
*/
public enum Lamp {
    /*南北方向东西方向的车交替放行,同一方向的等待车辆先放直行,然后放左转车辆。所以只需要考虑
     * 南北走向一个方向的路线和东西走向的一个方向的路线的交通灯情况,也就是4个灯,之后的其余的灯
     * 也按照这个模式方向就行了。
     * 
     * 同一路线先放行直行车辆,后放行左转车辆,所以同一走向应该同时放行两条相反直行路线,比如:南到北放行时,北到南的
     * 直行也在这时候放行。直行结束后,放行该路线的左转路线,该左转路线的相反方向也就是该直行路线的相反方向的左转路线。
     * 南到北路线的左转路线是南到西,南到西的相反路线就是北到东,以此类推。当南到北有相反方向路线时,北到南就没有相反
     * 方向了。所以每个灯除了要有状态,还要有相反路线的灯,下一个方向的灯。
     *
     */
    S2N(false,"N2S","S2W"),S2W(false,"N2E","E2W"),E2W(false,"W2E","E2S"),E2S(false,"W2N","N2S"),
    N2S(false,null,null),N2E(false,null,null),W2E(false,null,null),W2N(false,null,null),
    //4条右转路线的交通灯,状态常绿,没有相反方向的灯和下一个灯的规定
    S2E(true,null,null),E2N(true,null,null),N2W(true,null,null),W2S(true,null,null);
    
    //每个交通灯要有自己的状态,因为只考虑绿灯和红灯,使用布尔类型变量,灯亮(true)表示绿灯,灯黑(false)表示红灯
    private boolean state;
    private String oppoiste = "";
    private String next = "";
    
    //枚举的构造函数
    private Lamp(boolean state,String opposite,String next) {
        this.state = state;
        this.oppoiste = opposite;
        this.next = next;
    }
    
    //判断灯的状态
    public boolean islighte() {
        return state;
    }
    
    //某一路线的灯变亮时,并让与自己相反方向的灯也变亮。让自己相反方向的灯变来亮的前提是必须存在下一个灯
    public void light() {
        this.state = true;
        if(oppoiste != null) {
            Lamp.valueOf(oppoiste).light();
        }
        System.out.println(name() + "方向的灯变绿了,应该有6个方向的车开始行驶了");
    }
    
    /*某一路线的灯变黑时,它相反方向的灯也变黑,前提是要有相反方向的灯。
     * 并且让下一个灯变亮,前提也是存在下一个灯。下一个灯变亮后,返回下一个灯。
     */
    public Lamp toBlack() {
        this.state = false;
        if(oppoiste != null) {
            Lamp.valueOf(oppoiste).toBlack();
        }
        
        Lamp nextLamp = null;
        if(next != null) {
            nextLamp = Lamp.valueOf(next);
            System.out.println("绿灯由" + name() + "切换为:" + next);
            nextLamp.light();
        }
        return nextLamp;
    }
}

3,定义交通灯控制器:
它控制每个灯间隔一定时间将当前灯变亮,并将下一个灯变黑。在定义当前灯时,给他一个初始化值。

package com.yue.lamp;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/*
 * 定义一个交通灯的控制器,控制红绿灯交换时间。可以让每隔红绿灯的交换时间为固定的10秒。
 * 创建方法还是使用定时器,也就是一个可以在指定时间后执行指定的任务,并且后续可以周期执行该任务的线程池,调用
 * schedule()方法,传入任务和时间,周期,时间的单位
 */
public class LampController {
    //先拿到当前的灯
    private Lamp currentLamp;
    public LampController() {
        currentLamp = Lamp.S2N;
        //让当前灯变亮
        currentLamp.light();
        ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
        //每隔10秒将当前灯变黑,并将当前灯的下一个灯变绿
        timer.scheduleAtFixedRate(
                new Runnable() {
                    
                    @Override
                    public void run() {
                        currentLamp.toBlack();
                    }
                }, 
                10, 
                10, 
                TimeUnit.SECONDS);
    }
}

4,主函数,通过数组创12条路线实例对象。

package com.yue.lamp;
public class MainClass {
    public static void main(String[] args) {
        //创建12条路线数组,使用数组简化书写
        String[] name = {"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"};
        for(int i=0;i<name.length;i++) {
            new Road(name[i]);
        }
        new LampController();
    }
}