作为一个Java初学者来说,应该很难理解 interface 的作用,明明是个没有实现任何功能的东西,在API中却有那么多的接口类型,下面我将会用一个很小的计时器例子来简单展示下 interface 作用。
类库很简单,只有三个元素,类图如下:
Timer:是个计时器
ExecutionThread:是在计时器内使用的线程
ITask:即为计时器要执行的任务,而UserTask将会是用户为了给计时器指派任务而实现的接口
ITask 接口源码
package individual.hcx.generictimer;
/**
* 通用计时器的任务接口,为计时器指派任务,只需实现此接口中的task(),并用实现类初始化计时器即可
* @author hcx
*
*/
public interface ITask {
void task();
}
该接口很简单,只有一个方法需要实现,但该接口非常重要,后面会详细解释。
ExecutionThread源码
package individual.hcx.generictimer;
public class ExecutionThread extends Thread {
//线程休眠事件
private long period;
ITask task = null;
private boolean isRun = true;
/**
* 创建一个线程休眠事件为 period 毫秒的线程
* @param period
* @param task
*/
public ExecutionThread(long period , ITask task)
{
this.period = period;
this.task = task;
}
/**
* 结束线程执行
*/
public void stopTask()
{
this.isRun = false;
}
public void run() {
try
{
while(isRun)
{
//执行
task.task();
//当前线程休眠
Thread.sleep(period);
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
该线程类的实现也很容易看懂,其中加了一个状态字,以控制线程的终止。
在这个执行线程中,最为重要的就是在run()方法中执行的task.task();这其实就是面向接口编程的一种方式,准确来说,在编写ExecutionThread代码的时候,我们仅仅只知道一个抽象的方法,但是我们却能够调用它。在ExecutionThread中我们其实并不关心task的具体实现,我们只要知道他有这样的方法就行了,这就是抽象的具体应用。
Timer的源码
package individual.hcx.generictimer;
public class Timer {
long period = 0;
ExecutionThread executionThread = null;
/**
* 使用计时器的执行间隔初始化一个计时器
* @param period 执行间隔,以毫秒为单位,该值最小为1,如果传入小于1的值将还会是1
* @param task 计时器中执行的任务
*/
public Timer(long period , ITask task)
{
if(period <= 0)
this.period = 1;
else
this.period = period;
this.executionThread = new ExecutionThread(period, task);
}
/**
* 开始计时器,该方法只能调用一次
*/
public void start()
{
this.executionThread.start();
}
/**
* 结束计时器,一旦结束将无法重启,该方法只能调用一次
*/
public void stop()
{
this.executionThread.stopTask();
}
}
这段代码是Timer的主体实现,它定义了两个行为,start和stop,用于开始和结束计时器。Timer的构造器是需要一个ITask对象来初始化的,在这里我们还是不需要关心ITask的实现。
至此,其实我们的类库已经开发完毕,这个时候完全可以把我们的代码编译打包,生成一个Jar给别人使用,那别人如何使用呢?这里我写了一个场景类来模拟。
package individual.hcx.generictimer;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Timer timer = new Timer(1000 , new MyTask());
timer.start();
}
}
class MyTask implements ITask
{
int time = 1;
public void task() {
// TODO Auto-generated method stub
System.out.println(time);
time ++;
}
}
首先,我们需要实现一个ITask的实现类,在这个实现类中我们需要给计时器指派任务,也就是实现task()方法,我的实现很简单,只是输出一个数字。有了ITask的实现类,我们可以通过这个实现类实例化一个Timer对象,然后将它启动,这个时候你会发现,计时器可以很好的工作。当然啦,你完全可以再ITask的实现类中实现更复杂的方法,甚至说,你可以创建更多的ITask的实现类,然后来实例化更多的Timer对象,而这个时候你可以完全不用关心Timer的内部结构!
那么,interface 在这个程序中到底起了什么作用呢?功能隔离和编码分工!
计时器的功能是,能够以一定的周期执行某个特定的任务,一定的周期我们可以用一个long的变量来指定,但是特定的任务我们怎么处理呢?没错,就是用 interface 来抽象。于是,计时器的功能与具体的任务充分隔离开来了,既然功能隔离了,那么编码自然就可以分工了。负责编写计时器的的人,可以完全只去考虑计时器部分的实现。而实现指定任务的人,可以完全只去考虑任务的设计。只要这两拨人,遵循这个接口,那么在Client这样的高层结构中就能很好的集成,以完成一个复杂系统的开发。
最后,这个简单的计时器,其实可以理解为一个非常简单的框架,它通过接口来约束,客户在框架中的行为。当然啦,在实际中比较成功的框架,不仅仅只有接口这一个约束,但接口在任何一个框架中都是大量存在的!