结合 JDK 源码看设计模式 -- 模板方法模式 前言:
相信很多人都听过一个问题: 把大象关进冰箱门, 需要几步?
第一, 把冰箱门打开; 第二, 把大象放进去; 第三, 把冰箱门关上. 我们可以看见, 这个问题的答案回答的很有步骤. 接下来我们介绍一种设计模式 -- 模板方法模式, 你会发现, 它与这个问题的答案实际上有很多共同之处.
一, 定义
定义一个算法骨架, 允许子类为一个或多个步骤提供实现. 模板方法使得子类可以在不改变算法结构的情况下, 重新定义算法的某些步骤.
二, 适用场景 一次性实现一个算法的不变的部分, 将可变的行为留给子类实现
也就是将各子类中公共行为被提取出来并集中到一个公共父类中, 从而避免代码重复. 还是拿上面大象放进冰箱里面的例子, 打开冰箱和关上冰箱都是不变的行为, 我们可以将其放在公共父类实现. 但是放大象, 怎么放? 是先放背对着冰箱放, 还是面对着冰箱放. 不想放大象, 放老虎或者其他动物呢? 这些就是我们可变的行为, 这个就放入子类中实现. 可以说, 模板方法提供了一个很好的代码复用平台
三, JDK 中的模板方法模式
vc/W1Nq9srW9wcvEo7Dlt723qMSjyr2jrNTZyKW/tEFycmF5TGlzdLXEyrG68sTcw/ew17K7ydmho9Tax7DD5rXEsqm/zdbQ09DM4bW9TGlzdL3Tv9q6zUNsb25lYWJsZb3Tv9rKx9PDwLTKtc/WyrLDtMnovMbEo8q9tcSho73xzOy+zcC0v7S/tNXiuPZBcnJheUxpc3S1xLi4wOBBYnN0cmFjdExpc3ShozwvcD4KPHA+oaGhoUFic3RyYWN0TGlzdL7NysfO0sPHx7DD5srK08Ozob6w1tC96cnctcS4uMDgo6jSsr3QxKOw5cDgo6mjrNXiuPbA4MDvw+a8tMzhuanBy7mrubK1xLe9t6ijqLK7v8m5qdfTwODQ3rjEo6mjrNPWzOG5qcHLv8nIw9fTwODQ3rjEtcS3vbeooaPPwsPmztLDx9axvdO/tNS0wuujrNPJ09q3vbeouty24KOsztLDx77NvenJ3NK7z8JhZGRBbGy3vbeooaM8L3A+Cgo8cHJlIGNsYXNzPQ=="brush:java;">public abstract class AbstractList extends AbstractCollection implements List { public boolean addAll(int index, Collection c) { rangeCheckForAdd(index); boolean modified = false; for (E e : c) { add(index++, e); modified = true; } return modified; } }
上面的是 AbstractList 的 addAll 方法, 可以看见这个方法没有限定子类是否去修改, 子类由需要就去修改, 如果子类不想修改, 完全能够按照 AbstractList 中的逻辑添加元素. 事实上我看了一遍 AbstractList 中的方法发现除了一些私有的方法不能给子类给子类访问之外, 其余的基本上都是可以给子类去选择是否修改的. 如果子类觉得父类的方法可行, 那么直接使用父类的方法即可.
- public class ArrayList extends AbstractList
- implements List, RandomAccess, Cloneable, java.io.Serializable
- {
- public boolean addAll(int index, Collection<? extends E--> c) {
- rangeCheckForAdd(index);
- Object[] a = c.toArray();
- int numNew = a.length;
- ensureCapacityInternal(size + numNew); // Increments modCount
- int numMoved = size - index;
- if (numMoved> 0)
- System.arraycopy(elementData, index, elementData, index + numNew,
- numMoved);
- System.arraycopy(a, 0, elementData, index, numNew);
- size += numNew;
- return numNew != 0;
- }
- }
上面这个就是 ArrayList 中的 addAll 方法.
但是 AbstractList 里面有一个方法, 就是 get() 方法, AbstractList 明确要求要让子类实现. 由于代码较少, 我就直接截图
左边是父类 AbstractList 中的, 右边是 ArrayList 中的方法. 在父类中没有直接写出实现代码, 而是让子类自己手动去实现. 除此之外其实还有一个方法就是 AbstractList 父类 AbstractCollection 中的 toString 方法. 在 ArrayList 中是没有的, 但是平常在写代码时候, 是可以直接调用的, 这就是一个公共的方法.
四, 总结
模板方法模式只需要简单的继承关系就可以完成. 相信平常我们在写代码的时候也是使用过模板方法模式, 只是我们并不知道是这种设计模式. 这里多说一下, 如果我们希望子类不要修改父类的方法, 只需要加上 final 修饰即可; 如果希望子类一定重写父类的方法, 就将父类的方法用 abstract 修饰; 如果子类可以修改也可以不修改, 就可以像 addAll 方法那样设计即可. 重点理解模板, 这个模板尽量使用抽象类. 因为抽象类比接口更加的灵活, 能将模板定义的更好. 其实看完上面的源码解析, 总结起来就是一句话 AbstractList 是 ArrayList 的模板.
来源: https://www.2cto.com/kf/201904/801324.html