设计模式--模板方法模式&迭代器模式

162 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

模板方法模式

模板方法模式(Template Pattern)。通过一个抽象父类定义方法执行的方式、步骤,其实现类按需实现方法,通过抽象父类的调用,实现对子类的调用,属于行为型设计模式。

比如说我们有一套算法来计算男生和女生的体重指标(超重、正常、偏轻),对于男生和女生来说其算法肯定是不同的,假设我们计算体重的指标的算法是 体脂率/身高。那么我们可以将计算体脂率和计算身高的方法抽象出来定义为两个抽象方法,然后定义一个具体方法去调用这两个方法来计算体重指标,而对于男女生的具体实现抽象父类不用关系心,对于具体实现只需要遵循抽象类的规范即可,这样可以约束子类、并简化子类实现。

涉及角色

  • 一个抽象父类
  • 实现抽象类的实现类

实现

可以参考java集合框架中的设计。我们以集合的增删为例。

定义抽象模板类,可以有具体实现,定义调用规则:

public abstract class AbstractCollection {
    /**
     * 获取首个元素
     * @return String.class
     */
    abstract String getFirstElement();
    /**
     * 默认获取首个元素
     * @return
     */
    String getElement(){
       return getFirstElement();
    }
}

定义两个实现类,实现抽象模板类,并实现对应方法:

@Data
public class ArrayListCollection extends AbstractCollection {
    private ArrayList<String> list = new ArrayList<>();
    {
        list.add("a");
        list.add("b");
        list.add("c");
    }
    @Override
    String getFirstElement() {
        return list.get(0);
    }
}
@Data
public class LinkedListCollection extends AbstractCollection {
    private LinkedList<String> list = new LinkedList();
    {
        list.add("a");
        list.add("b");
        list.add("c");
    }
    @Override
    String getFirstElement() {
        return list.getFirst();
    }
}

测试:

@Test
public void test() {
    AbstractCollection linkedListCollection = new LinkedListCollection();
    //调用抽象夫类的模版方法,实现子类方法的调用
    System.out.println(linkedListCollection.getElement());
    AbstractCollection arrayListCollection = new ArrayListCollection();
    //调用抽象夫类的模版方法,实现子类方法的调用
    System.out.println(arrayListCollection.getElement());
}

image-20220617124930941.png

应用

模板方法在java源码中存在大量应用,像AbstractQueuedSynchronizer(队列同步器),以及大量的在集合框架中使用。由模板类来简化子类的实现,并约束子类的行为。


迭代器模式

迭代器模式(Iterator Pattern)属于行为型模式。适用于顺序访问容器对象的元素,无需了解其底层实现,就可以很方便的遍历出集合的元素。

java集合框架对于有序的集合基本上都实现类自己的迭代器。

对于Collection这一簇,会有Iterable接口定义实现规范。对于Map和Set也会有对应的实现。

ArrayList的Iterater

我们可以参考List接口下的集合对于迭代器的实现加以理解,这里使用ArrayList作为例子,主要看一下hasNext和next这两个方法。

迭代器接口:

对于遍历集合来说重要的就是下面两个方法:

  • hashNext用于判断迭代是否完成
  • next用于获取后继元素
public interface Iterator<E> {
    boolean hasNext();
    E next();
}

作为内部类组合进ArrayList,对于ArrayList来说这不是一个线程安全的集合,所以在迭代和遍历的时候我们都会存在一个检查线程安全的方法来避免并发带来的危害。

private class Itr implements Iterator<E> {
    int cursor;       
    int lastRet = -1; 
    int expectedModCount = modCount;
    Itr() {}
    //判断是否存在后继元素
    public boolean hasNext() {
        return cursor != size;
    }
		//获取下一个元素
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
}

仿造实现

模仿List集合对于Iterable的实现。

定义抽象迭代器接口,简单实现之需要关心next()和hashNext()方法:

public interface Iiterator<E> {
    /**
     * 判断集合是否还存在元素
     * @return Boolean.class
     */
    Boolean hashNext();

    /**
     * 获取下一个元素
     * @return E
     */
    E next();
}

定义自定义抽象集合接口

public interface ICollection<E> {
    /**
     * 提供获取迭代器的方法
     *
     * @return E
     */
    Iiterator<E> iterator();
    /**
     * 获取集合首个元素
     *
     * @return
     */
    E getFirstElement();
    /**
     * 获取元素
     * @param index
     * @return
     */
    E get(int index);
}

自定义集合实现类

将迭代器作为内部类组合进来,由于我们简单实现,迭代器之需要一个当前下标数据即可实现迭代器迭代。需要注意的是迭代器作为自定义集合的内部类,不希望他暴露给外部,所以一般会用private修饰。

为了操作简单,内部借助ArrayList实现。

这里为了简化操作直接将内部属性ArrayList直接初始化。

public class MyCollection<E> implements ICollection<E> {

    private class MyCollectionIterator<E> implements Iiterator<E> {
        int currentIndex;
        public MyCollectionIterator() {
        }
        @Override
        public Boolean hashNext() {
            return arrayList.size() != currentIndex;
        }
        @Override
        public E next() {
            return (E) arrayList.get(currentIndex++);
        }
    }

    /**
     * 内部借助ArrayList实现
     */
    private ArrayList<E> arrayList = new ArrayList<>();
    {
        //初始化一些值
        for (int i = 0; i < 10; i++) {
            arrayList.add((E) Integer.valueOf(i));
        }
    }

    @Override
    public Iiterator<E> iterator() {
        return new MyCollectionIterator<>();
    }

    @Override
    public E getFirstElement() {
        return arrayList.get(0);
    }

    @Override
    public E get(int index) {
        return arrayList.get(index);
    }
}

测试:

@Test
public void testMyCollection() {
    MyCollection<Integer> objectMyCollection = new MyCollection<>();
    Iiterator<Integer> iterator = objectMyCollection.iterator();
    while (iterator.hashNext()){
        System.out.println(iterator.next());
    }
}

image-20220617232022033.png

我们好像实现了一个简单的集合类~~~,迭代器模式的大致意思就是这样,一般来说如果我们不会去使用迭代器模式,java为我们提供的集合框架已足够使用,并且Apache基金会对于java基础类的扩展也很全面,如果jdk提供的不满足使用也优先考虑使用轮子而不是造轮子。