首先它强调的是结构,结构图如下。
通过上图,可以看到它的结构有3部分组成,和现实当中的大树结构很类似。它包括有Root根节点(相当于大树主干)、树枝节点和叶子节点。根节点连接着树枝节点和叶子节点,而叶子节点不能在连接其他节点。
想象一颗大树是如何将营养供给给叶子的,它们是通过主干延伸的树枝最终作用到了叶子上,最终让叶子成长起来。树枝建立了树干和叶子之间的通道,并起到了单向传输的作用,而且能确保给到大树的每一个叶子上。
那好,我问你一个树干上有多少树枝,很简单你会数一下从树干上延伸了多少树枝,告诉我答案。我再问你一个树枝上有多少叶子,也很简单通过递归的方式也能得到最终的结果。最后我问你,让你通过程序模拟大树结构,并实现营养从树干传到叶子,你有想法了吗?先不要往下看,请先认真的想一想。
......
接下来,我们先看看该设计模式需要用到哪些对象,这点很重要。
Component(Composite和Leaf共同的抽象类)
Composite (主干和树枝的具体实现类)
Leaf (叶子的具体实现类)
通过这3个对象我们已经可以很好的实现一把了,代码如下:
java
import java.util.ArrayList;
import java.util.List;
//透明组合模式
public class ComponentLucency {
public static void main(String[] args) {
// 来一个根节点
Component root = new Composite("root");
// 来一个树枝节点
Component branchA = new Composite("---branchA");
Component branchB = new Composite("------branchB");
// 来一个叶子节点
Component leafA = new Leaf("------leafA");
Component leafB = new Leaf("---------leafB");
Component leafC = new Leaf("---leafC");
root.addChild(branchA);
root.addChild(leafC);
branchA.addChild(leafA);
branchA.addChild(branchB);
branchB.addChild(leafB);
String result = root.provide("营养");
System.out.println(result);
}
}
// 抽象根节点
abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public abstract String provide(String matter);
public boolean addChild(Component component) {
throw new UnsupportedOperationException("addChild not supported!");
}
public boolean removeChild(Component component) {
throw new UnsupportedOperationException("removeChild not supported!");
}
public Component getChild(int index) {
throw new UnsupportedOperationException("getChild not supported!");
}
}
// 树节点
class Composite extends Component {
private List<Component> mComponents;
public Composite(String name) {
super(name);
this.mComponents = new ArrayList<Component>();
}
@Override
public String provide(String matter) {
StringBuilder builder = new StringBuilder(this.name);
for (Component component : this.mComponents) {
builder.append("\n");
builder.append(component.provide(matter));
}
return builder.toString();
}
@Override
public boolean addChild(Component component) {
return this.mComponents.add(component);
}
@Override
public boolean removeChild(Component component) {
return this.mComponents.remove(component);
}
@Override
public Component getChild(int index) {
return this.mComponents.get(index);
}
}
//叶子节点
class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public String provide(String matter) {
return this.name+"得到了"+matter;
}
}
最终控制台输出效果如下:
css
root
---branchA
------leafA得到了营养
------branchB
---------leafB得到了营养
---leafC得到了营养
可以看到所有叶子都得到了营养,最终实现了目标。
上边的代码真的没有问题了吗,细心的你发现到了一个细节。
Leaf节点继承了Component抽象类,却没有实现他的addChild...其他抽象方法,那么在leaf调用的时候,可能不小心就调用了这些没有实现的抽象方法,最终抛出异常,而这种实现方式可以称为透明组合模式。
那该方法如何避免呢,请看下列代码:
java
package design.component;
import java.util.ArrayList;
import java.util.List;
//安全组合模式
public class ComponentSafeMode {
public static void main(String[] args) {
// 来一个根节点
CompositeSafe root = new CompositeSafe("root");
// 来一个树枝节点
CompositeSafe branchA = new CompositeSafe("---branchA");
CompositeSafe branchB = new CompositeSafe("------branchB");
// 来一个叶子节点
ComponentSafe leafA = new LeafSafe("------leafA");
ComponentSafe leafB = new LeafSafe("---------leafB");
ComponentSafe leafC = new LeafSafe("---leafC");
root.addChild(branchA);
root.addChild(leafC);
branchA.addChild(leafA);
branchA.addChild(branchB);
branchB.addChild(leafB);
String result = root.provide("营养");
System.out.println(result);
}
}
// 抽象根节点
abstract class ComponentSafe {
protected String name;
public ComponentSafe(String name) {
this.name = name;
}
public abstract String provide(String matter);
}
// 树节点
class CompositeSafe extends ComponentSafe {
private List<ComponentSafe> mComponents;
public CompositeSafe(String name) {
super(name);
this.mComponents = new ArrayList<ComponentSafe>();
}
@Override
public String provide(String matter) {
StringBuilder builder = new StringBuilder(this.name);
for (ComponentSafe component : this.mComponents) {
builder.append("\n");
builder.append(component.provide(matter));
}
return builder.toString();
}
public boolean addChild(ComponentSafe component) {
return this.mComponents.add(component);
}
public boolean removeChild(ComponentSafe component) {
return this.mComponents.remove(component);
}
public ComponentSafe getChild(int index) {
return this.mComponents.get(index);
}
}
//叶子节点
class LeafSafe extends ComponentSafe {
public LeafSafe(String name) {
super(name);
}
@Override
public String provide(String matter) {
return this.name+"得到了"+matter;
}
}
上述实现可以看到Component抽象类原本的addChild...方法移到了Composite具体实现类中,也就避免了刚才那个问题,而这种模式称为安全组合模式。
那你可能就要问了这种模式它有什么优缺点呢,实际应用场景是怎样的,源码的体现呢。
好处显而易见就是结构稳定,保证传输没有问题,而最大的缺点就是概念比较抽象不易理解和维护,而且调用链不易过深。
实际应用中,比如维护一个目录结构,我们是不是就可以采用这种方式。
至于源码的体现呢,想想Mybatis映射文件中是否存在很多的动态标签,它们是不是也可以使用该设计模式。而事实上Mybatis就是这样做的,如下图所示:
结束!