应用程序框架(application framework)是被设计用来解决某类特定问题的一个类或一组类。要运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。在覆盖后的方法中,编写代码定制应用程序提供的通用解决方案,以解决你的特定问题。模板方法包含算法的基本结构,并且会调用一个或多个可覆盖的方法,以完成算法的动作。设计模式总是将变化的事物与保持不变的事物分离开,在这个模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。
控制框架(control framework)是一类特殊的应用程序框架,它用来解决相应事件的需求。主要用来响应事件的系统被称作事件驱动系统。
在本文中,保持不变的事物即模版方法是控制框架,变化的事物即可覆盖的方法是温室控制系统。
一、控制框架模版
我们首先需要定义一个事件(Event)文件,事件文件中主要包含事件的”就绪”状态,以判断什么时候开始执行相对应的动作,但是动作我们写成抽象(abstract)方法即可,让子类去具体实现其方法。代码如下:
package content.ch10;
public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime) {
this.delayTime = delayTime;
start();
}
public void start() {
eventTime = System.nanoTime() + delayTime;
}
public boolean ready() {
return System.nanoTime() >= eventTime;
}
public abstract void action();
}
上述事件代码依据构造函数中的延迟时间(delayTime)变量来决定什么时候触发事件,start()是一个独立的方法,而没有包含在构造器内,因为这样就可以在事件运行以后重新启动计时器,也就是能够重复使用Event对象。例如,如果想要重复一个事件,只需简单地在action()中调用start()方法。ready()告诉你何时能够运行action()方法,当然,也可以在导出类中覆盖ready()方法,使得Event能够基于时间之外的其他因素而触发。
接下来,我们需要写我们的事件的控制器(实际控制框架),以用来管理并触发事件的发生。代码如下:
package content.ch10;
import java.util.ArrayList;
import java.util.List;
public class Controller {
private List<Event> eventList = new ArrayList<Event>();
public void addEvent(Event c) {
eventList.add(c);
}
public void run() {
while (eventList.size() > 0) {
// Make a copy so you're not modifying the list
// while you're selecting the elements in it.
for (Event e : new ArrayList<Event>(eventList)) {
if (e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
}
}
/////////////////////////// Backup///////////////////////////////
public void run_V1() {
while (eventList.size() > 0) {
// 如果不做eventList的备份,eventList.get(0)之后,remove(0)操作后,
// 原来的索引1移到索引0处,原来的索引2移到索引1处,那么第二次循环取索引1的操作,
// 会变成取原来的索引2,即跳过了真正的索引1。即选择其中的元素后,还要进行删除操作,
// 会影响第二次取值,所以需要进行一个备份。
for (int i = 0; i < eventList.size(); i++) {
if (eventList.get(i).ready()) {
System.out.println(eventList.get(i));
eventList.get(i).action();
eventList.remove(i);
}
}
}
}
}
上述中事件的列表要做备份,Backup后面已经解释清楚了原因。
run()方法循环遍历eventList,寻找就绪的(ready())、要运行的Event对象。对找到的每一个就绪的(ready())事件,使用对象的toString()打印其信息,调用其action()方法,然后从队列中移除此Event。
在目前的设计中,我们只是把控制框架写好了,包括事件(Event)和控制器(Controller),但你并不知道Event到底做了什么,这正是此设计的关键所在,“使变化的事物与不变的事物分离”。“变化的事物”就是各种不同的Event对象所具有的不同行为,而你可以通过创建不同的Event子类来表示不同的行为。
而这正是内部类擅长之处,内部类允许:
(1).控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装了起来。内部类用来表示解决问题所必需的各种不同的action()。
(2).内部类能够很容易地访问外围类的任意成员,所以可以避免这种实现变得笨拙。
二、温室控制系统
通过此控制框架的一个特定实现:温室控制系统,来说明内部类的具体作用。
此温室控制系统可以控制温室的运作,比如:控制灯光、水、温度调节器的开关,以及响铃和重新启动系统,每个行为都是完全不同的。控制框架的设计使得分离这些不同的代码变得非常容易,使用内部类,可以在单一的类里面产生对同一个基类Event的多种导出版本。对温室系统的每一种行为,都继承一个新的Event内部类,并在要实现的action()中编写控制代码。代码如下:
package content.ch10;
public class GreenhouseControls extends Controller {
private boolean light = false;
public class LightOn extends Event {
public LightOn(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here to
// physically turn on the light.
light = true;
}
public String toString() {
return "Light is on";
}
}
public class LightOff extends Event {
public LightOff(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here to
// physically turn off the light.
light = false;
}
public String toString() {
return "Light is off";
}
}
private boolean water = false;
public class WaterOn extends Event {
public WaterOn(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here
water = true;
}
public String toString() {
return "Greenhouse water is on";
}
}
public class WaterOff extends Event {
public WaterOff(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here
water = false;
}
public String toString() {
return "Greenhouse water is off";
}
}
private String thermostat = "Day";
public class ThermostatNight extends Event {
public ThermostatNight(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here.
thermostat = "Night";
}
public String toString() {
return "Thermostat on night setting";
}
}
public class ThermostatDay extends Event {
public ThermostatDay(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here.
thermostat = "Day";
}
public String toString() {
return "Thermostat on day setting";
}
}
// An example of an action() that inserts a
// new one of itself into the event list.
public class Bell extends Event {
public Bell(long delayTime) {
super(delayTime);
}
public void action() {
addEvent(new Bell(delayTime));
}
public String toString() {
return "Bing!";
}
}
public class Restart extends Event {
private Event[] eventList;
public Restart(long delayTime, Event[] eventList) {
super(delayTime);
this.eventList = eventList;
for (Event e : eventList) {
addEvent(e);
}
}
public void action() {
for (Event e : eventList) {
e.start(); // Rerun each event
addEvent(e);
}
start(); // Rerun this Event
addEvent(this);
}
public String toString() {
return "Restarting system";
}
}
public static class Terminate extends Event {
public Terminate(long delayTime) {
super(delayTime);
}
public void action() {
System.exit(0);
}
public String toString() {
return "Terminating";
}
}
}
在实际中,可以在action()中加入相对应的硬件控制代码来实际上控制相应的物理行为。
最后,我们需要配置该温室控制系统,通过添加各种不同的Event对象来进行配置。代码如下:
package content.ch10;
public class GreenhouseController {
public static void main(String[] args) {
GreenhouseControls gc = new GreenhouseControls();
//Instead of hard-wiring,you could parse
//configution information from text file here.
gc.addEvent(gc.new Bell(900));
Event[] eventList = {
gc.new ThermostatNight(0),
gc.new LightOn(200),
gc.new LightOff(400),
gc.new WaterOn(600),
gc.new WaterOff(800),
gc.new ThermostatDay(1400)
};
gc.addEvent(gc.new Restart(2000,eventList));
/* // 从命令行接受输入
if(args.length == 1){
gc.addEvent(new GreenhouseControls.Terminate(new Integer(args[0])));
}*/
gc.addEvent(new GreenhouseControls.Terminate(new Integer(100)));
gc.run();
}
}
这个类的作用是初始化系统,它添加了所有的相应的事件。Restart事件反复运行,而且它每次都会将eventList加载到GreenhouseControls对象中。如果提供了命令行参数,系统会以它作为毫秒数,决定什么时候终止程序。运行该程序结果如下:
Bing!
Thermostat on night setting
Light is on
Light is off
Greenhouse water is on
Greenhouse water is off
Thermostat on day setting
Restarting system
Terminating
至此,我们应该能很清楚的意识到内部类的实际作用,内部类很像多重继承,比如:Bell和Restart拥有Event的所有方法,并且似乎也拥有外围类GreenhouseControls的所有方法。使用内部类,我们可以很优雅的实现温室控制系统,通过模板(不变的事物)—控制框架,具体实现(变化的事物)—温室控制系统,我们很好的实现了温室控制系统的功能,并且当我们后期想要加一些功能,比如:加一个打开、关闭风扇的功能,我们只需在GreenhouseControls.java中增加两个控制风扇打开、关闭的内部类即可,然后在GreenhouseController.java中配置以使用新加的Event对象即可,对于控制框架模板完全不需要修改任何地方。
来自:《Java编程思想》第十章 内部类与控制框架