空接口能做些什么?

自从有了接口的概念后,OO编程都推荐面向接口编程。根据“如非必要,勿增实体”的原则,通常我们定义(或重构出来)的接口都是有行为的,很少用空接口。那么空接口有什么用呢?
一个接口定义了两方面,类别和特征。比如


public interface Animal{

         void eat();

         void sleep();

}


定义了类别Animal,它的特征是:可以eat和sleep。如果我们不关心sleep,这个接口就变成


public interface Animal{

         void eat();

}



这两个Animal描述的是现实中的同一类别,只是关心的角度不同,抽取出的特征也不同。

更进一步,如果连eat这个特征我们都不关心,这个接口就变成了一个空借口


public interface Animal{ 

}




定义这样一个借口相当于阐述以下事实“尽管现在不关心动物具体有什么特征,但本接口的存在说明系统中动物和非动物是有区别的,以后也可能会用到这些区别”。

这样,Tree不用实现Animal,但Dog应该实现Animal。空命名接口的存在对类的设计给出了约束,即方便了以后扩展,也让后来者理解代码更加方便。可以认为是“代码即文档”的一种体现。



-------------------------------------------------------------------------------------------------------------------------

在通常的业务系统中,分页查询是非常常用的功能。对这样的查询,在用Toplink(Hibernate应该也一样)实现时一个最佳实践是不用对象,而用SQL实现。

假设User包括id,name,birthday,gander,department,isActive几个属性,在“维护用户”界面上需要分页显示id,name,birthday,gander。那么DAO不要返回一个Page对象给Service,而应该自定义一个PagingUserResult

public class PagingUserResult{

         private String id;

         private String name;

         private Date birthday;

         private Gander gander;

         //.. Getter and Setter

}

并返回Page<PagingUserResult>给Service。这是因为

1)Toplink组装对象的时间比较长,特别是如果在UnitOfWork中作这件事,每个User会被Clone一份到UnitOfWork中,整体时间消耗要多不少。

2)分页功能牵涉的数据量是比较大的,如果User组装成对象,一旦翻上几页,这些实际不会操作到的对象就会把缓存中的User都挤出缓存,使得缓存命中率非常低,使得用缓存不如不用。

所以,设计框架时要求分页查询都用ReportQuery,不允许出现直接返回Page<User>给Service的情况。当然,这样的规范必须通知Service的实现者。这可以通过文档,code review等方式,不过最好的方法是在框架中尽量杜绝Service这样作的可能性,这时候空接口就派上作用了。

 

首先定义一个空接口

public interface QueryResult{
}

由于具体的QueryResult(比如PagingUserResult)就是个Bean,所以QueryResult没有共同行为,只是一个标识作用。

然后,定义Page接口

 

public interface Page<T extends QueryResult> {
    T get(int index);     
     //.... other functions
}

 

这里,通过泛型表达了“Page只能容纳QueryResult”,由于User是Domain对象,不会实现QueryResult,自然不可能被放到Page中了。

 

当然,由于Page只是一个接口。为了使用方便,框架还要提供一个PageBuilder,这里是一个大概的样子

public class PageBuilder<T extends QueryResult> {
       private List<T> contents=new ArrayList<T>();
       public PageBuilder<T> add(T content) {
        contents.add(content);
        return this;
    }
       public Page<T> buildPage(){
         // You may need a inner class which implements Page<T> here

      }
}

 

总结一下,结合空接口QueryResult和泛型,我们避免了developer以后由于不了解而误用的情况。

类似的,也可以让Domain对象实现一个叫作Entity的空接口,可以规范DAO的使用,避免像DAO中传入非Domain的Class。

 

你可能感兴趣的:(DAO,编程,框架,Hibernate,OO)