最近工作中发生了一个事故:网站和APP都依赖于底层服务的一个接口,网站是通过Dubbo调用,APP则是Jar包依赖的方式。因该接口调用次数频繁、极端情况下会把该接口调爆,底层服务的同事就把该接口改成了批量接口。网站和APP的代码都改成了调用批量的接口,测试环境下前后端代码都同步上,测试都通过了。上线那天,网站和APP的主线代码都发现合了测试未完结不能上线的代码,而网站要上线一个活动必须发包。决定网站的包增量上,APP没有该活动则不发包。增量时因为要手动排除不能上线的代码,耗费了一定的时间。
但底层服务的包在前端同事不知情的情况下就已经发布了,突然发现未发包的网站已经调用不掉原接口了,底层服务的同事没有保留老接口重新写批量接口,而是直接在老接口上修改了! 顿时大家都傻眼了…
就这样,底层服务的同事违反了“开闭原则”:对修改关闭,对扩展开放;不删老接口,只新增接口!
上包无序不联动是一个问题;违反开闭原则是一个问题。但代码如果写的好,比如采用了模板方法、策略模式等,也能低成本而自觉地遵循开闭原则。今天就先说说模板方法对开闭原则的体现。
如下代码采用了模板方法。抽象父类确定了一个算法所需的关键步骤,并确定了这些步骤的执行顺序,见process()方法。但是某些步骤的具体实现是未知的,例如method2()只在该父类中实现,抽象方法method3()在子类中实现了,method1()在父类中实现而子类又重写了。
public abstract class AbstractTemplate {
public final void process() {
method1();
method2();
method3();
}
public void method1() {
System.out.println("fatherClass method1 is executing...");
};
public void method2() {
System.out.println("fatherClass method2 is executing...");
};
public abstract void method3();
}
public class ConcreteTemplate extends AbstractTemplate {
@Override
public void method1() {
System.out.println("sonClass method1 is executing...");
}
@Override
public void method3() {
System.out.println("sonClass method3 is executing...");
}
public void pointcut() {
super.process();
}
public static void main(String[] args) {
ConcreteTemplate jvmkHook = new ConcreteTemplate();
jvmkHook.pointcut();
}
}
执行结果是:
sonClass method1 is executing…
fatherClass method2 is executing…
sonClass method3 is executing…
发现子类调用父类的process()方法,除了子类未重新的method2()方法,都执行了子类自己实现或覆盖的方法。抽象类定义了一个操作中的算法骨架,将一些步骤声明为抽象方法迫使子类去实现。子类通过继承这个抽象基类以不同的方式实现这些抽象方法,而工作流程却由父类控制(定义方法为final),同时遵循了开闭原则!