打字猴:1.70048397e+09
1700483970 IUserSpecification userSpec2=new UserByAgeThan(20);
1700483971
1700483972 userList=userProvider.findUser(userSpec1);
1700483973
1700483974 for(User u:userProvider.findUser(userSpec2)){
1700483975
1700483976 System.out.println(u);
1700483977
1700483978 }
1700483979
1700483980 }
1700483981
1700483982 }
1700483983
1700483984 能够实现,但是思考一下程序逻辑,它采用了两次过滤,也就是两次循环,如果对象数量少还好说,如果对象数量巨大,这个效率就太低了,这是其一;其二,组合方式非常多,比如“与”、“或”、“非”可以自由组合,姓名中包含“国庆”但年龄小于25的用户,姓名中不包含国庆但年龄大于25岁的用户等等,我们还能如此设计吗?太多的组合方式,产生组合爆炸,这种设计就不妥了,应该有更优秀的方案。
1700483985
1700483986 我们换个方式思考该问题,不管是AND或者OR或者NOT操作,它们的返回结果都还是一个规格书,只是逻辑更复杂了而已,这3个操作符只是提供了对原有规格书的复合作用,换句话说,规格书对象之间可以进行与或非操作,操作的结果不变,分析到这里,我们就可以开始修改接口了,如代码清单37-15所示。
1700483987
1700483988 代码清单37-15 带与或非的规格书接口
1700483989
1700483990 public interface IUserSpecification{
1700483991
1700483992 //候选者是否满足要求
1700483993
1700483994 public boolean isSatisfiedBy(User user);
1700483995
1700483996 //and操作
1700483997
1700483998 public IUserSpecification and(IUserSpecification spec);
1700483999
1700484000 //or操作
1700484001
1700484002 public IUserSpecification or(IUserSpecification spec);
1700484003
1700484004 //not操作
1700484005
1700484006 public IUserSpecification not();
1700484007
1700484008 }
1700484009
1700484010 在规格书接口中增加了与或非的操作,接口修改了,实现类当然也要修改。先全面思考一下业务,与或非是不可扩展的操作,规格书(也就是规格对象)之间的操作只有这三种方法,是不需要扩展也不用预留扩展空间的。如此,我们就可以把与或非的实现放到基类中,那现在的问题变成了怎么在基类中实现与或非。注意看它们的返回值都需要返回规格书类型,很明显,我们在这里要用到递归调用了。可以这样理解,基类需要子类提供业务逻辑支持,因为基类是一个抽象类,不能实例化后返回,我们把简单类图画出来,如图37-3所示。
1700484011
1700484012
1700484013
1700484014
1700484015 图37-3 与规格的示意
1700484016
1700484017 基类对子类产生了依赖,然后进行递归计算,大家一定会发出这样的疑问:父类怎么可能依赖子类,这还是面向接口编程吗?想想看,我们提出面向接口编程的目的是什么?是为了适应变化,拥抱变化,对于不可能发生变化的部分为什么不能固化呢?与或非操作符号还会增加修改吗?规格书对象之间的操作还有其他吗?思考清楚这些问题后,答案就迎刃而解了。
1700484018
1700484019 注意 父类依赖子类的情景只有在非常明确不会发生变化的场景中存在,它不具备扩展性,是一种固化而不可变化的结构。
[ 上一页 ]  [ :1.70048397e+09 ]  [ 下一页 ]