ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### [内部类与控制框架](https://lingcoder.gitee.io/onjava8/#/book/11-Inner-Classes?id=%e5%86%85%e9%83%a8%e7%b1%bb%e4%b8%8e%e6%8e%a7%e5%88%b6%e6%a1%86%e6%9e%b6) 在将要介绍的控制框架(control framework)中,可以看到更多使用内部类的具体例子。 应用程序框架(application framework)就是被设计用以解决某类特定问题的一个类或一组类。要运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。在覆盖后的方法中,编写代码定制应用程序框架提供的通用解决方案,以解决你的特定问题。这是设计模式中模板方法的一个例子,模板方法包含算法的基本结构,并且会调用一个或多个可覆盖的方法,以完成算法的动作。设计模式总是将变化的事物与保持不变的事物分离开,在这个模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。 控制框架是一类特殊的应用程序框架,它用来解决响应事件的需求。主要用来响应事件的系统被称作*事件驱动*系统。应用程序设计中常见的问题之一是图形用户接口(GUI),它几乎完全是事件驱动的系统。 要理解内部类是如何允许简单的创建过程以及如何使用控制框架的,请考虑这样一个控制框架,它的工作就是在事件“就绪”的时候执行事件。虽然“就绪”可以指任何事,但在本例中是指基于时间触发的事件。接下来的问题就是,对于要控制什么,控制框架并不包含任何具体的信息。那些信息是在实现算法的`action()`部分时,通过继承来提供的。 首先,接口描述了要控制的事件。因为其默认的行为是基于时间去执行控制,所以使用抽象类代替实际的接口。下面的例子包含了某些实现: ~~~ // innerclasses/controller/Event.java // The common methods for any control event package innerclasses.controller; import java.time.*; // Java 8 time classes public abstract class Event { private Instant eventTime; protected final Duration delayTime; public Event(long millisecondDelay) { delayTime = Duration.ofMillis(millisecondDelay); start(); } public void start() { // Allows restarting eventTime = Instant.now().plus(delayTime); } public boolean ready() { return Instant.now().isAfter(eventTime); } public abstract void action(); } ~~~ 当希望运行**Event**并随后调用`start()`时,那么构造器就会捕获(从对象创建的时刻开始的)时间,此时间是这样得来的:`start()`获取当前时间,然后加上一个延迟时间,这样生成触发事件的时间。`start()`是一个独立的方法,而没有包含在构造器内,因为这样就可以在事件运行以后重新启动计时器,也就是能够重复使用**Event**对象。例如,如果想要重复一个事件,只需简单地在`action()`中调用`start()`方法。 `ready()`告诉你何时可以运行`action()`方法了。当然,可以在派生类中覆盖`ready()`方法,使得**Event**能够基于时间以外的其他因素而触发。 下面的文件包含了一个用来管理并触发事件的实际控制框架。**Event**对象被保存在**List** 类型(读作“Event 的列表”)的容器对象中,容器会在[集合](https://lingcoder.gitee.io/onjava8/#/)中详细介绍。目前读者只需要知道`add()`方法用来将一个**Event**添加到**List**的尾端,`size()`方法用来得到**List**中元素的个数,foreach 语法用来连续获取**List**中的**Event**,`remove()`方法用来从**List**中移除指定的**Event**。 ~~~ // innerclasses/controller/Controller.java // The reusable framework for control systems package innerclasses.controller; import java.util.*; public class Controller { // A class from java.util to hold Event objects: private List<Event> eventList = new ArrayList<>(); 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<>(eventList)) if(e.ready()) { System.out.println(e); e.action(); eventList.remove(e); } } } ~~~ `run()`方法循环遍历**eventList**,寻找就绪的(`ready()`)、要运行的**Event**对象。对找到的每一个就绪的(`ready()`)事件,使用对象的`toString()`打印其信息,调用其`action()`方法,然后从列表中移除此**Event**。 注意,在目前的设计中你并不知道**Event**到底做了什么。这正是此设计的关键所在—"使变化的事物与不变的事物相互分离”。用我的话说,“变化向量”就是各种不同的**Event**对象所具有的不同行为,而你通过创建不同的**Event**子类来表现不同的行为。 这正是内部类要做的事情,内部类允许: 1. 控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装了起来。内部类用来表示解决问题所必需的各种不同的`action()`。 2. 内部类能够很容易地访问外部类的任意成员,所以可以避免这种实现变得笨拙。如果没有这种能力,代码将变得令人讨厌,以至于你肯定会选择别的方法。 考虑此控制框架的一个特定实现,如控制温室的运作:控制灯光、水、温度调节器的开关,以及响铃和重新启动系统,每个行为都是完全不同的。控制框架的设计使得分离这些不同的代码变得非常容易。使用内部类,可以在单一的类里面产生对同一个基类**Event**的多种派生版本。对于温室系统的每一种行为,都继承创建一个新的**Event**内部类,并在要实现的`action()`中编写控制代码。 作为典型的应用程序框架,**GreenhouseControls**类继承自**Controller**: ~~~ // innerclasses/GreenhouseControls.java // This produces a specific application of the // control system, all in a single class. Inner // classes allow you to encapsulate different // functionality for each type of event. import innerclasses.controller.*; public class GreenhouseControls extends Controller { private boolean light = false; public class LightOn extends Event { public LightOn(long delayTime) { super(delayTime); } @Override public void action() { // Put hardware control code here to // physically turn on the light. light = true; } @Override public String toString() { return "Light is on"; } } public class LightOff extends Event { public LightOff(long delayTime) { super(delayTime); } @Override public void action() { // Put hardware control code here to // physically turn off the light. light = false; } @Override public String toString() { return "Light is off"; } } private boolean water = false; public class WaterOn extends Event { public WaterOn(long delayTime) { super(delayTime); } @Override public void action() { // Put hardware control code here. water = true; } @Override public String toString() { return "Greenhouse water is on"; } } public class WaterOff extends Event { public WaterOff(long delayTime) { super(delayTime); } @Override public void action() { // Put hardware control code here. water = false; } @Override public String toString() { return "Greenhouse water is off"; } } private String thermostat = "Day"; public class ThermostatNight extends Event { public ThermostatNight(long delayTime) { super(delayTime); } @Override public void action() { // Put hardware control code here. thermostat = "Night"; } @Override public String toString() { return "Thermostat on night setting"; } } public class ThermostatDay extends Event { public ThermostatDay(long delayTime) { super(delayTime); } @Override public void action() { // Put hardware control code here. thermostat = "Day"; } @Override 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); } @Override public void action() { addEvent(new Bell(delayTime.toMillis())); } @Override 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); } @Override public void action() { for(Event e : eventList) { e.start(); // Rerun each event addEvent(e); } start(); // Rerun this Event addEvent(this); } @Override public String toString() { return "Restarting system"; } } public static class Terminate extends Event { public Terminate(long delayTime) { super(delayTime); } @Override public void action() { System.exit(0); } @Override public String toString() { return "Terminating"; } } } ~~~ 注意,**light**,**water**和**thermostat**都属于外部类**GreenhouseControls**,而这些内部类能够自由地访问那些字段,无需限定条件或特殊许可。而且,`action()`方法通常都涉及对某种硬件的控制。 大多数**Event**类看起来都很相似,但是**Bell**和**Restart**则比较特别。**Bell**控制响铃,然后在事件列表中增加一个**Bell**对象,于是过一会儿它可以再次响铃。读者可能注意到了内部类是多么像多重继承:**Bell**和**Restart**有**Event**的所有方法,并且似乎也拥有外部类**GreenhouseContrlos**的所有方法。 一个由**Event**对象组成的数组被递交给**Restart**,该数组要加到控制器上。由于`Restart()`也是一个**Event**对象,所以同样可以将**Restart**对象添加到`Restart.action()`中,以使系统能够有规律地重新启动自己。 下面的类通过创建一个**GreenhouseControls**对象,并添加各种不同的**Event**对象来配置该系统,这是命令设计模式的一个例子—**eventList**中的每个对象都被封装成对象的请求: ~~~ // innerclasses/GreenhouseController.java // Configure and execute the greenhouse system import innerclasses.controller.*; public class GreenhouseController { public static void main(String[] args) { GreenhouseControls gc = new GreenhouseControls(); // Instead of using code, you could parse // configuration information from a text file: 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)); gc.addEvent( new GreenhouseControls.Terminate(5000)); gc.run(); } } ~~~ 输出为: ~~~ Thermostat on night setting Light is on Light is off Greenhouse water is on Greenhouse water is off Bing! Thermostat on day setting Bing! Restarting system Thermostat on night setting Light is on Light is off Greenhouse water is on Bing! Greenhouse water is off Thermostat on day setting Bing! Restarting system Thermostat on night setting Light is on Light is off Bing! Greenhouse water is on Greenhouse water is off Terminating ~~~ 这个类的作用是初始化系统,所以它添加了所有相应的事件。**Restart**事件反复运行,而且它每次都会将**eventList**加载到**GreenhouseControls**对象中。如果提供了命令行参数,系统会以它作为毫秒数,决定什么时候终止程序(这是测试程序时使用的)。 当然,更灵活的方法是避免对事件进行硬编码。 这个例子应该使读者更了解内部类的价值了,特别是在控制框架中使用内部类的时候。