结合JDK源码看设计模式——模板方法模式
前言:
相信很多人都听过一个问题:把大象关进冰箱门,需要几步?
第一,把冰箱门打开;第二,把大象放进去;第三,把冰箱门关上。我们可以看见,这个问题的答案回答的很有步骤。接下来我们介绍一种设计模式——模板方法模式,你会发现,它与这个问题的答案实际上有很多共同之处。
一、定义
定义一个算法骨架,允许子类为一个或多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
二、适用场景
一次性实现一个算法的不变的部分,将可变的行为留给子类实现
也就是将各子类中公共行为被提取出来并集中到一个公共父类中,从而避免代码重复。还是拿上面大象放进冰箱里面的例子,打开冰箱和关上冰箱都是不变的行为,我们可以将其放在公共父类实现。但是放大象,怎么放?是先放背对着冰箱放,还是面对着冰箱放。不想放大象,放老虎或者其他动物呢?这些就是我们可变的行为,这个就放入子类中实现。可以说,模板方法提供了一个很好的代码复用平台
三、jdk中的模板方法模式
在刚接触arraylist的时候一直没注意它继承的类和实现的接口。直到现在讲到了模板方法模式,再去看arraylist的时候能明白不少。在前面的博客中有提到list接口和cloneable接口是用来实现什么设计模式的。今天就来看看这个arraylist的父类abstractlist。
abstractlist就是我们前面适用场景中介绍的父类(也叫模板类),这个类里面即提供了公共的方法(不可供子类修改),又提供了可让子类修改的方法。下面我们直接看源码,由于方法很多,我们就介绍一下addall方法。
public abstract class abstractlist<e> extends abstractcollection<e> implements list<e> { public boolean addall(int index, collection<? extends e> c) { rangecheckforadd(index); boolean modified = false; for (e e : c) { add(index++, e); modified = true; } return modified; } }
上面的是abstractlist的addall方法,可以看见这个方法没有限定子类是否去修改,子类由需要就去修改,如果子类不想修改,完全能够按照abstractlist中的逻辑添加元素。事实上我看了一遍abstractlist中的方法发现除了一些私有的方法不能给子类给子类访问之外,其余的基本上都是可以给子类去选择是否修改的。如果子类觉得父类的方法可行,那么直接使用父类的方法即可。
public class arraylist<e> extends abstractlist<e> implements list<e>, 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的模板。