接口应用实例----回调(callback)

接口在面向对象编程中应用极广。回调(CallBack)就是一个典型的示例。

先解释一下回调的概念。

通常情况下,我们创建一个对象,并马上直接调用它的方法。然而,在有些情况下,希望能在某个场景出现后或条件满足时才调用此对象的方法。回调就可以解决这个“延迟调用对象方法”的问题。这个被调用方法的对象称为回调对象。

实现回调的原理简介如下:

首先创建一个回调对象,然后再创建一个控制器对象,将回调对象需要被调用的方法告诉控制器对象。控制器对象负责检查某个场景是否出现或某个条件是否满足。当此场景出现或此条件满足时,自动调用回调对象的方法。

可以举个现实生活中的例子。

一读者想借《编程的奥秘——.NET软件技术学习与实践》这本书,但这本书已被其他读者借走了。于是,读者与图书馆管理员间发生了以下对话:

读者:“我把我的电话号码告诉你,等书一到就马上通知我。”

管理员:“好的。另一读者把书还回来后,马上给您打电话,书我先帮您留着。”

在上述这个场景中,读者就是“回调对象”,管理员就是“控制器对象”,读者的电话号码就是“回调对象的方法”,另一读者的还书事件就是“某一特定的场景”。

本节示例CallBackExamples展示了如何使用接口实现回调,此示例运行截图如图4-9所示。

图4-9  回调示例

程序运行时,从键盘上按任意一个键显示当前时间,整个程序可以“没完没了”地运行下去,除非您按了Esc键。

示例项目CallBackExamples的类图如图4-10所示。

ICallBack接口定义了一个run()方法。

//实现回调的类必须实现此接口

public interface ICallBack
{ void run();}

CallBackClass类实现此接口,并在其run()方法中向控制台窗口输出当前时间。

图4-10  CallBackExamples示例项目类图

class CallBackClass:ICallBack

{public void run()
{ //输出当前时间
 System.Console.WriteLine(DateTime.Now );
}
}

Controller类中有一个私有的ICallBack类型的字段,用于存放回调对象的引用,此对象引用在构造函数中传入。

class Controller

{

    public ICallBack CallBackObject = null;// 引用回调对象

    public Controller(ICallBack obj)

    {

        this.CallBackObject = obj;

    }

    public void Begin()

    {

        Console.WriteLine("敲任意键显示当前时间,ESC键退出....");

        while (Console.ReadKey(true).Key != ConsoleKey.Escape)

        {

            CallBackObject.run();

        }

    }

}

Controller类的Begin()方法启动整个处理过程。

调用代码如下:

class Program

{

    static void Main(string[] args)

    {

        //创建控制器对象,将提供给它的回调对象传入

        Controller obj = new Controller(new CallBackClass());

        //启动控制器对象运行

        obj.Begin();

    }

}

可以看到,当示例程序运行时,何时调用CallBackClass对象的的run()方法是由用户决定的,用户每敲一个键,控件器对象就调用一次CallBackClass对象的的run()方法。在这个示例中,实现回调的关键在于ICallBack接口的引入。

读者可能在想,如果不用ICallBack接口,而直接使用CallBackClass对象,也可以实现同样的运行效果。

class Controller

{

    public CallBackClass CallBackObject = null;//回调对象的方法引用

    public Controller(CallBackClass obj)

    {

        this.CallBackObject = obj;

    }

    //……

}

但请仔细想一下,这样做的结果就使Controller类与CallBackClass对象绑定在一起,万一如果需要回调其他类型的对象,则必须修改Controller类的代码(本示例中至少得同时修改Controller类CallBackObject字段的数据类型与构造函数参数的数据类型两处代码)。

如果Controller类接收的是一个抽象的接口变量ICallBack,则任何一个实现了此接口的对象都可以被Controller类对象所回调,Controller类的代码可以保持稳定,无疑是一个好的设计方案。

事实上,接口可以看成是一个契约,它规定了某个对象必须“是什么样的”,即“接口所规定的方法,实现此接口的类一定有”。正是有了这种确定性,Controller类才成为一个用键盘来“发出”回调的“万能控制器”。

 

试一试:

(1)请读者再创建另外一个回调类,让Controller类回调它。

(2)请修改Controller类,让它一次可以回调多个对象或多种类型的对象。

 

提示:

(1)将Controller类的CallBackObject字段改为可容纳多个ICallBack对象的容器,比如数组、ArrayList等。

(2)利用本书13.1.3节中介绍的委托,也能实现同样的回调功能。

5.接口与抽象类的区别

小结一下接口与抽象类的区别。

q 抽象类是一个不完全的类,需要子类来完善它。

q 接口只是对类的约束,它仅仅承诺了类能够调用的方法。

q 一个类一次可以实现若干个接口,但一个类只能继承一个父类。

在实际编程中,接口的使用要比抽象类广泛得多。

posted @ 2008-04-16 15:04  系咪噶  阅读(3077)  评论(2编辑  收藏  举报