我们先看一下java.awt.container这么一个类,
public class Container extends Component
我们可以看到这个类继承Component,awt这个包下边都是实现窗口菜单,属于编写C/S结构所使用的,我们可以通过行为方法进行
识别,它是将相同的抽象类的类型,或者接口的类型,这种类型的实例转换为树状结构,他本身Container继承了Component,我们
再看一下,他的add方法,
public Component add(Component comp) {
addImpl(comp, null, -1);
return comp;
}
他的add方法增加了一个Component,和我们是一样的,课程目录里面增加了一个自己的父类,这个就是通过行为方法来识别,同理我们
来看一下java.util.HashMap,HashMap大家都很熟悉,
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
他呢实现了Map接口,同时我们再看一下他的putAll方法,
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}
putAll这个方法里面放的参数呢,也是一个Map,这里也是组合模式的一个应用,同理我们再看一下大家非常熟悉的,java.util下的List,
我们直接看实现ArrayList,
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
同理他也是实现了List接口,然后看一下addAll这个方法,
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
addAll里面直接添加了一个Collection,和Collection是什么关系呢,他实现了List接口,我们进来
public interface List<E> extends Collection<E>
List接口又继承了Collection接口,所以ArrayList里面的addAll,这个方法也正是组合模式的一个应用,那set也是同理,
那JDK讲完了,我们再看一下在Mybatis当中,有什么应用呢,我们看一个类,叫sqlnode,通过这个名字,就可以看出来,
这个就代表一个SQL节点,我们看一下,
package org.apache.ibatis.scripting.xmltags;
/**
* @author Clinton Begin
*/
public interface SqlNode {
boolean apply(DynamicContext context);
}
组合模式就是把多个对象,组合成一个对象,简化对多个对象的访问,对于Mybatis的MYSQL,一条SQL可以被解析成多个SQLNODE对象,
SqlNode对象,有什么实现呢,我们Ctrl + T看一下,这里面有很多,我们使用了很多Mybatis的SQL,无论是集合的遍历,还是
where语句的一个拼装,其实使用的都是SqlNode的这么一个父类,只不过对于Mybatis而言,各种SQL都会解析成不同的SqlNode
对象,他们都属于同一个类型的对象,都实现了SqlNode接口,所以他们都是SqlNode对象,多个SqlNode怎么结合到一块呢,
我们看一下MixedSqlNode,这个就是他们的核心,这个就相当于我们coding实战当中的课程目录,课程目录里面有很多课程,
和这个是同理,我们可以看到他实现了SqlNode这个接口
public class MixedSqlNode implements SqlNode {
private List<SqlNode> contents;
public MixedSqlNode(List<SqlNode> contents) {
this.contents = contents;
}
@Override
public boolean apply(DynamicContext context) {
for (SqlNode sqlNode : contents) {
sqlNode.apply(context);
}
return true;
}
}
同时里面还包含了多个SqlNode,这个就是CourseCatalog是一模一样的,CourseCatalog他继承了抽象类CatalogComponent,
只不过我们使用的是抽象类,而Mybatis使用的是接口,然后里面的items,相当于MixedSqlNode的contents,然后下边的apply
也是循环遍历,然后挨个进行apply,把上下文再传进来,那这个apply和我们现在的print方法,是异曲同工的,通过这么一个类比,
相信这一块一定会理解更深入的,那我们接着回来,所以我们在使用组合模式的时候,最关键的一点是什么呢,就是叶子对象,
和组合对象,都要实现相同的接口,或者继承相同的抽象类,他们之间是要有这种关系的,只有达到这样的关系,组合模式才能将叶子节点
对象,还有组合好的对象,这种节点进行一致的处理,那我们再来看一下,其他的实现比如WhereSqlNode,
public class WhereSqlNode extends TrimSqlNode {
private static List<String> prefixList = Arrays.asList("AND ","OR ","AND\n", "OR\n", "AND\r", "OR\r", "AND\t","OR\t");
public WhereSqlNode(Configuration configuration, SqlNode contents) {
super(configuration, contents, "WHERE", prefixList, null, null);
}
}
可以看到这里面有一些他的实现,比如他的前缀list,包括AND,OR,还有带\n的AND,OR,还有带\r的,还有带\t的,他的构造器就调用
父类的构造器,
然后把这些条件传进来,比如prefix直接写了一个"WHERE",父类就是调用TrimSqlNode,我们点进来
public TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, String prefixesToOverride, String suffix, String suffixesToOverride) {
this(configuration, contents, prefix, parseOverrides(prefixesToOverride), suffix, parseOverrides(suffixesToOverride));
}
他直接调用了这个构造器,这个构造器又调用了this,我们进来看一下
protected TrimSqlNode(Configuration configuration, SqlNode contents, String prefix, List<String> prefixesToOverride,
String suffix, List<String> suffixesToOverride) {
this.contents = contents;
this.prefix = prefix;
this.prefixesToOverride = prefixesToOverride;
this.suffix = suffix;
this.suffixesToOverride = suffixesToOverride;
this.configuration = configuration;
}
this就把这些值赋值上
apply的时候进行使用
@Override
public boolean apply(DynamicContext context) {
FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
boolean result = contents.apply(filteredDynamicContext);
filteredDynamicContext.applyAll();
return result;
}
所以对于SqlNode这么一个接口,可以说是开源框架中对于组合模式描述非常清晰的一个开源框架源码,希望你们如果有时间,
来看一下,SqlNode其他节点的具体实现,那我们可以通过UML看源码的,可以快速的对你关心的源码结构有所了解,
我们可以右键点开show implementation,show实现,这里面都是实现这个接口的类,我们直接按Ctrl + T挨个选一下,平时呢我
也是使用这种办法,来学习一些源码,现在这个类比较多
这里刚好放满一屏,局部放大,SetSqlNode继承了TrimSqlNode,然后上面的都实现了SqlNode接口,然后我们打开他们的关系
可以看一下,有些类是组合关系,有些类并不是组合关系,那我们可以对这些有差异的类,还有这些字段属性,方法名字,都打开
之后,我们来看一下,那现在字体非常非常小,我们可以通过这种方式,来对整个SqlNode的实现呢,一目了然,因为我们学习了UML,
来看这些图还是很方便的,这个也是一个非常好的办法,来学习源码,希望你们能够学习这个技能,那组合模式在我们日常开发工作中,
一定要注意我们coding过程中,碰到的那几个坑,还有刚才讲到的要有统一的接口实现,或者统一的抽象父类