• 结合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的模板。

  • 相关阅读:
    SELinux安全方式
    PHP jpgraph的一点小提示和方法
    PHP之文件的锁定、上传与下载的方法
    MySQL与Oracle差异函数对比
    Dictionary 初始化数据
    IIS7的集成模式下如何让自定义的HttpModule不处理静态文件(.html .css .js .jpeg等)请求
    iis 负载均衡
    iis 反向代理 组件 Application Request Route
    语法糖
    vs git 推送远程会失败.
  • 原文地址:https://www.cnblogs.com/Cubemen/p/10682708.html
Copyright © 2020-2023  润新知