• 2012-07-11

    delegatej

    Tag:delegatej

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/219423077.html

    缘起

    在Java里,为了启动一个线程,我们要创建一个实现Runnable接口的类:

    public class MyParallelComputer implements Runnable {
      pulbic void run() {
        ...
      }
    }

    new Thread(new MyRunner()).start();

    再有,为了响应一个按钮的事件,我们要创建一个ActionListener接口的类:

    button.add(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        ...
      }
    });

    所有这些,其实,我们需要的并不是一个类,而是满足一定约定的调用接口,只是因为我们用的是Java,让我们别无选择。做同样的事情,对比其它程序设计语言:

    • 很多程序设计语言里有lambda,甚至连方法名都可以省掉
    • C#里有delegate,只要方法的入参和返回值兼容,方法名也不重要
    • 即便是C,用函数指针,也只限定了入参和返回值,方法名也不重要

    对比来看,Java的实现有两点堪称多余,类型和方法名。

    在面向对象设计中,继承一种非常强的关系。虽然在语法层面上,我们只是简单地extends或implements一个类型,但实际上,背后隐藏着好多概念,比如is-a,比如LSP,所以,继承关系必须让我们小心翼翼,确保继承真的是继承。

    再来看方法名,从之前几种程序设计语言的对比来看,对于这个调用接口而言,我们真正关心的只是入参和返回值,而方法名并不是我们的重点,而且,在现代程序设计中,方法名扮演着文档的作用,而实现这样的调用接口,我们不得不遵循这个死板的名字。所以,现实中经常的做法是,有一个按照自己意图命名的方法,在这个死板的方法中调用。

    好吧,说了Java实现诸多的问题,接下来自然是要解决这样的问题,一起看看我们的主角:delegatej。

    起步走

    delegatej是一个基于annotation实现的delegate。从前面的介绍里面,你已经知道delegatej要解决问题是什么了。下面我们就来看看,如何应用delegatej。

    假设我们有一个要实现的接口

    public interface Executable {
        String execute(String name);
    }

    既然是一个基于annotation的实现,我们自然要有一个annotation,它就是给方法做标记的:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Handle {
    }

    再来,我们有一个要实现Executable接口的方法:

    public class Runner {
        @Handle
        public String hello(String name) {
            return "hello " + name;
        }
    }

    与实现接口不同的是,这里只是用annotation标记了方法,没有强硬的继承关系,方法名也与接口标记的完全不同,相同的只是入参和返回值而已。要把这样的方法对接到接口上,就是delegatej发挥作用的地方了:

      Executable executable = delegate(Handle.class).to(Executable.class).trait(new Runner());
      executable.execute("dreamhead"); // "hello dreamhead"

    这段代码的意思很清楚,把Handle这个annotation标记的方法委托给(delegate to)Executable接口的实现,然后,用这个约定从一个对象(new Runner())压榨(trait)出对应的方法来。这样一来,我们就得到了一个Executable接口的实现,也就可以当做Executable来用了,执行这个方法时,就会调用到hello方法。

    同样,那个启动线程的例子就可以稍微修改一下:

    public class MyParallelComputer {
      @Parallel
      pulbic void compute() {
        ...
      }
    }

    Runnable runnable = delegate(Parallel.class).to(Runnable.class).trait(new MyParallelComputer());
    new Thread(runnable).start();

    至于那个响应按钮事件的例子,就留给你做练习了。

    分享到:
    引用地址: