文章目录
1、前言
体能状态先于精神状态,习惯先于决心,聚焦先于喜好。
2、本文的灵感
最近的几个星期笔者在工作开发和Sping 源码学习中看到了一些“新奇”的代码写法,与此产生了一些灵感。
开发中遇到的情况是这样的,笔者需要对一个对账功能进行改造,现有程序有两个特点,一个是对账功能无法指定时间,由定时任务调用,总是生成昨天的定时任务;第二个特点是由于对接了多家公司,而每个公司对账形式又不相同。改造的点在于需要提供一个接口,允许按照指定日期生成对账文件,但是不能影响原有的功能。
正常来说,我需要新增一个以时间作为入参的方法即可。但是这样做的话每个公司的对账逻辑我都需要进行变动——系统现存的定时任务调用的方法并没有入参。
惊奇的是我发现代码结构使用了 接口-父类-子类 的形式,其中父类中提供了 private 修饰的时间参数和 get、set 方法,与此同时接口中有给时间赋值的抽象方法
这样一来我的改造思路就变为了-先调用给时间赋值的方法,然后调用原先的对账文件的方法,跟踪代码逻辑可以发现,需要将系统中获取时间的地方全部由“获取昨天”修改为“如果时间参数为空则获取昨天,否则就获取指定的时间参数”-在工具类中增加这个方法即可,然后重构测试后就搞定了
在查看Spring 源码的过程中,发现了这样的情况,Spring 的顶层是接口,有些接口甚至是没有抽象方法的,然后这些接口被子类实现,子类又有新的子类,这样也可以简化出一个 接口-父类-子类的结构,而接口可以使用父类或者子类实例化,而子类可以复用父类中的方法。这对于Spring 从不同的来源加载配置文件提供了极大的方便
3、给出一个UML图
依据上面讨论的 接口-父类-子类 模型,笔者做了进一步的延伸,就是提供两个接口,并且这两个接口中拥有一个一模一样的抽象方法。
3.1、提出第一个问题
InterfaceA 和 InterfaceB 中拥有相同的抽象方法 sayHello(),类 FirstClass 需要重写几个 sayHello()方法?
答案是一个。 这意味着,如果我们只是想调用 sayHello()方法,使用InterfaceA 或者 InterfaceB 来进行Spring 注入效果是一样的。
3.2、提出第二个问题
InterfaceA 可否被 Sub1Class 实例化(或者进行Spring注入),FirstClass是否可以被Sub1Class 实例化(或者进行Spring注入)?
答案是:可以;可以。从子类实现接口和子类继承父类来说,这都是Java 继承机制的体现,但是有一点是需要注意的,Spring 默认使用的是 JDK 动态代理,需要借助于接口进行注入,虽然Spring 也支持cglib 动态代理,但是在进行FirstClass 注入Sub1Class时有可能会报无法找到代理类的问题。
3、接口-父类-子类 模型的好处
这种模型的好处在于,当我们新增一个功能的时候可以通过一个新的接口去声明,然后在父类中实现,而子类可以直接调用,但是不需要做额外的工作,对于功能扩展来说对代码的改动是非常友好的。
4、拓展 接口-抽象类-子类
对于复杂业务逻辑,同时存在 批量操作和单个操作的场景,可以使用下面的模型进行扩展。
4.1、复杂业务可以拆分为多个子逻辑。
从灵活性和简洁性的角度看,分为逻辑与行为的关系,子逻辑行为之间的关系。
逻辑和行为可以拆开。
子逻辑之间的关系可以选择平铺——固定逻辑且逻辑比较简单。也可以借鉴链思维,子逻辑之间分别独立,行为触发下一个子逻辑的调用。
4.2、批量和单个处理出入参类型不同
可以使用 接口+实现类,或者 接口+抽象类+实现类的方式解决
接口(定义泛型)+实现类(具体类型)
// 接口(定义泛型)
public interface DemoInterface<T,K>{
K check2(T t);
}
//实现类(具体类型)
public class Demo1Class implements DemoInterface<List,Boolean> {
public Boolean check2(List list) {
return null;
}
}
接口(定义泛型)+抽象类(定义泛型)+实现类(具体类型)
//接口(定义泛型)
public interface DemoInterface<T,K>{
K check2(T t);
}
//抽象类(定义泛型)
public abstract class DemoAbstract<T,K> implements DemoInterface<T,K> {
public abstract K check(T t);
}
//实现类(具体类型)
public class Demo1Class implements DemoInterface<List,Boolean> {
public Boolean check2(List list) {
return null;
}
}
5、Jetty 抽象设计参考
将 Http 服务器+servlet容器进行分开抽象和扩展。
第一层:Handler->AbstracHandler
第二层:AbstracHandlerContainer
第三层: HandlerWrapper(过滤器型)、HandlerCollection(协调型-选择为某一个具体的)
第四层: HandlerWrapper子类(内容型)、HandlerCollection子类(内容型)
6、子类覆盖父类的逻辑
抽象父类提供了通用逻辑的写法,子类实现特殊化的逻辑。
但是特殊情况,如果父类提供的基本方法不适用,子类依旧可以覆盖父类的方法——尽管这样会带来一些代码阅读上的障碍。