Java内部类实例:控制框架(e.g.温室控制系统)

       应用程序框架(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编程思想》第十章 内部类与控制框架

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值