面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法。
再牢牢记住几个案例就可以了:
人在黑板上画圆——>圆心跟半径都是圆本身的属性,所以应该由圆本身提供此方法
列车司机紧急刹车
售货员统计收获小票的金额——>金额是小票上的属性所以此方法该在小票上
你把门关上
小球从绳子的一段移动到绳子的另一端
两块石头磨成石刀,石刀砍树,做成椅子。
(如果将一个对象变为另一个对象的方法一般不会放在原本对象类中,而用工厂模式
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 block
e.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() {
// 将当前灯状态改为红、false
this.lighted = false;
// 将对应方向的灯也改为false
if (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() {
// 将当前灯状态改为红、false
this.lighted = false;
// 将对应方向的灯也改为false
if (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();
}
}