论模板模式

模板方法模式的实现方案比对
问题:模板方法模式的主要意义在于规范算法流程。标准的模板方法模式采用抽象类的实现方案。但规范流程更像是规范行为。而抽象类与接口相比,抽象类侧重于属性,接口侧重于行为。再者java8开始,接口中可以编写默认方法,因此模板方法模式可以采用抽象类和接口两种实现方案。那么到底哪种实现方案比较合适呢?
需求:按指定流程推送数据。流程如下:
从数据库中查询数据 ---> 封装数据 ---> 推送
标准模板方法模式的写法
抽象模板类(抽象类):规范数据的使用流程

/** * Created by r.x on 2019/4/5. * 抽象模板 */ public abstract class Template {/** * 数据使用流程: * 1、查库 * 2、封装数据 * 3、推送数据 */ public final void use() { List list = queryDb(); JSONObject json = packData(list); push(json); }/** * 从数据库查询数据 * * @return */ protected abstract List queryDb(); /** * 按要求 封装数据 * * @param list * @return */ protected abstract JSONObject packData(List list); /** * 通用的数据推送方法 * @param json */ private void push(JSONObject json) { System.out.println(json); } }

string的具体使用实现
/** * Created by r.x on 2019/4/5. * string的具体使用实现 */ public class PushString extends Template {@Override protected List queryDb() { return Collections.singletonList("string"); }@Override protected JSONObject packData(List list) { StringJoiner joiner = new StringJoiner(","); list.forEach(joiner::add); JSONObject json = new JSONObject(); json.put("type", "string"); json.put("data", joiner.toString()); return json; } }

integer的具体使用实现
/** * Created by r.x on 2019/4/5. * integer的具体使用实现 */ public class PushInteger extends Template {@Override protected List queryDb() { return Collections.singletonList(0); }@Override protected JSONObject packData(List list) { JSONObject json = new JSONObject(); json.put("type", "integer"); json.put("data", list); return json; } }

入口类:
/** * Created by r.x on 2019/4/5. * 入口类 */ public class Demo {public static void main(String[] args) { Template pushString = new PushString(); pushString.use(); Template pushInteger = new PushInteger(); pushInteger.use(); } }

输出结果
{"data":"string","type":"string"} {"data":[0],"type":"integer"}

用接口的方式实现模板方法模式
抽象模板(接口)
/** * Created by r.x on 2019/4/5. * 抽象模板 */ public interface ITemplate { /** * 数据使用流程: * 1、查库 * 2、封装数据 * 3、推送数据 */ default void use() { List list = queryDb(); JSONObject json = packData(list); push(json); }/** * 从数据库查询数据 * * @return */ List queryDb(); /** * 按要求 封装数据 * * @param list * @return */ JSONObject packData(List list); /** * 通用的数据推送方法 * @param json */ default void push(JSONObject json) { System.out.println(json); } }

integer的具体使用实现
/** * Created by r.x on 2019/4/5. * integer的具体使用实现 */ public class PushIntegerImpl implements ITemplate {@Override public List queryDb() { return Collections.singletonList(-1); }@Override public JSONObject packData(List list) { JSONObject json = new JSONObject(); json.put("type", "integer impl"); json.put("data", list); return json; } }

string的具体使用实现
/** * Created by r.x on 2019/4/6. * string的具体使用实现 */ public class PushStringImpl implements ITemplate { @Override public List queryDb() { return Collections.singletonList("string impl"); }@Override public JSONObject packData(List list) { StringJoiner joiner = new StringJoiner(","); list.forEach(joiner::add); JSONObject json = new JSONObject(); json.put("type", "string impl"); json.put("data", joiner.toString()); return json; } }

入口类
/** * Created by r.x on 2019/4/5. * 入口类 */ public class DemoImpl {public static void main(String[] args) { ITemplate pushString = new PushStringImpl(); pushString.use(); ITemplate pushInteger = new PushIntegerImpl(); pushInteger.use(); } }

输出结果:
{"data":"string impl","type":"string impl"} {"data":[-1],"type":"integer impl"}

那么从输出结果可以看出,采用接口的方式也是可以实现同样的效果的
抽象类与接口方案的比对
【论模板模式】从以上对比可以看出,虽然接口方式也能实现模板方法模式,但接口中定义的方法完全暴露了,甚至连最重要的use()方法也被暴露了,因为子类可以覆盖接口中的默认方法,此时就失去了规范流程的意义了。因此,即使技术上可行,但也不推荐使用接口的实现方案。

    推荐阅读