“设计模式参见”系列模式讲解,初学同学可参考观看,方便入门,本篇主要对开源项目中相关设计模式的实际应用做分析,加深同学们在实际应用中对相关模式的理解。
一家之言,欢迎大家指正!原创不易,转载请注明出处,感谢!
文章链接:https://editor.csdn.net/md/?articleId=105540394
类适配器模式
对象适配器模式
接口适配器模式(非权威博文见,正确性待确认,单开说)
挂羊头,卖狗肉,顾客要羊,不要狗。
但是,顾客的目的只是拿到肉,而不会去管是”狗肉“还是”羊肉“。
怎么将“狗肉”挂到“羊头”底下,就是适配器需要干的事情。
区别:
类适配器模式:适配器对象通过继承”狗肉“(被适配者)对象,将”羊头“(目标对象)与”狗肉“适配,顾客看到是”羊头“,然后去拿到肉。
对象适配器模式:适配器对象将”狗肉“(被适配者)对象定义到自身内部,然后将”羊头“(目标对象)与”狗肉“适配,顾客看到是”羊头“,然后去拿到肉。
uml图例:
JDK1.1之前的集合(collection)类型(如:Vector、Stack、Hashtable)都实现了一个elements的方法,该方法会返回一个Enumeration类型对象:
interface Enumeration{
hasMoreElements();//集合中是否还有更多元素
nextElement();//拿集合的下一个元素
}
JDK1.2时,Sun退出更新后的集合类,开始使用Iterator(迭代器)接口:
interface Iterator{
hasNext();//可取代hasMoreElements()
next();//可取代nextElement()
remove();
}
这俩接口功能很相似,不同的是Iterator还多提供了一个remove()方法,当我们面临1.1的遗留代码时,这些代码暴露出Enumeration接口,但是我们在1.2版本中只希望使用Iterator,面对这个问题,我们需要构造适配器。
接着我们对照着上面的uml图例,此时的目标接口即为:Iterator接口(羊头),此时的被适配者接口即为:Enumeration(狗头),此时构造我们的适配器要实现目标接口,hasNext()、next()很好实现,直接对应过去就好,remove()接口如何实现呢?
因为Enumeration(枚举)不支持删除,该接口即为”只读“接口,适配器无法实现一个有实际功能的remove()方法,最多抛出一个运行时异常,比较屌的是,迭代器接口设计者事先就考虑到了这种需要,所以把remove()方法定义为:
default void remove() {
throw new UnsupportedOperationException("remove");
}
小试牛刀搞一下?同学们可以自己写一个EnumeratorIterator适配器,试试?
大家应该都知道Arrays.asList()这个方法,作用是将数组转为List集合。接下来我们展开讲讲,这个是怎么应用适配器模式的。
先看看Arrays.asList长啥样:
public class Arrays {
//...
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
/**将数组转为一个ArrayList集合,此处的ArrayList是作为一个内部类出现的,和 *java.util.ArrayList相比有哪些不同
* 1、注意下构造函数,看看有哪些不同
* 2、注意下复写的方法,看看少了那些
*/
}
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a; //狗肉出现了
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
//...
}
}
和上面一样,我们照着uml图例一起往下分析
将数组转为List集合
1、顾客要使用集合;
2、我们实际有的是一个数组;
3、我们要做适配,将数组是配成集合供顾客使用
所以我们清楚了一件事:羊头即为:List 狗肉即为:数组[]
然后我们看看适配的过程是怎么完成的
这个适配的过程其实是在Arrays类里面的内部类ArrayList的里面实现的,通过将数组对象定义在适配器里面 private final E[] a;去完成相应的适配过程,这种方式也就是我们上面提到的对象是配方式。
我们通过比较Arrays内部实现的ArrayList里面复写的方法,发现少了几个remove、add、clear等等,这也就是为什么在使用asList转换得到的集合,如果调用以上的三个方法会抛异常(UnsupportedOperationException),大家可以自己点开源码往下看看为什么。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*
* This implementation always throws an
* {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
throw new UnsupportedOperationException();
}
}