组合模式的目的是用一种统一的方式来处理整体和部分的关系,常见的就是树形结构,叶子节点和包含叶子节点的组合节点之间,有着不同的处理逻辑,常见的就是组合节点拥有一个叶子节点的集合,可以添加或者删除叶子结点,而叶子节点没有这个功能,所以为了统一处理这种逻辑,就出现了组合的设计模式。具体来说,组合的设计模式有以下两个实现方式:
1. 透明式的组合逻辑
透明的组合逻辑的本质思想是将叶子节点完全当成一个组合几点来使用,和组合节点拥有相同的类结构,只是在叶子节点实现的时候,将用不到的组合节点的逻辑抛出不支持的异常。
透明式组合模式的实现类图
透明式组合逻辑的代码示例
1. 先创建一个抽象类Component,定义好所有的相关的方法(包含了组合类addChild和removeChild方法),并将所有的方法实现都抛出UnsupportedOperationException异常
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public void print(String prefix) {
throw new UnsupportedOperationException("not support this operate");
}
public void addChild(Component component) {
throw new UnsupportedOperationException("not support this operate");
}
public void removeChild(Component component) {
throw new UnsupportedOperationException("not support this operate");
}
}
- 实现叶子节点Leaf,继承Component类,只重写叶子节点相关的方法,其他组合结点的方法继承父类不支持的实现
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void print(String prefix) {
System.out.println(prefix + " -" + this.name);
}
}
- 创建组合对象Composite,继承Component类,并重写相关的方法
public class Composite extends Component {
private List<Component> children = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void print(String prefix) {
if (children.size() == 0) {
System.out.println(prefix + " -" + this.name);
} else {
System.out.println(prefix + " +" + this.name);
for (Component child : children) {
child.print(prefix + " ");
}
}
}
@Override
public void addChild(Component component) {
children.add(component);
}
@Override
public void removeChild(Component component) {
children.remove(component);
}
}
- 创建一个测试类
public class Client {
@Test
public void testTransParentComposite(){
Component root = new Composite("root");
root.addChild(new Composite("empty Composite"));
root.addChild(new Leaf("level 1 leaf"));
Component secondComposite = new Composite("second Composite");
secondComposite.addChild(new Leaf("second leaf"));
root.addChild(secondComposite);
root.print("");
}
}
测试结果如下:
+root
-empty Composite
-level 1 leaf
+second Composite
-second leaf
透明组合模式总结
从上面的代码可以看出,透明式组合逻辑的思想就是把叶子节点和组合节点都当成组合节点来对待,其顶层的父类中定义了所有的方法,并默认实现了抛出不支持异常的逻辑,在这种实现中,叶子节点默认也是继承了Component对象的所有方法,只是和自己不相关的不支持而已。这种写法的好处就是在使用的时候,叶子结点和组合节点创建的时候都是可以赋值给顶层的Component节点,不需要区别对待;坏处就是叶子结点也暴露了组合节点的逻辑,如果调用,会抛出异常,这不符合最小知道原则的设计思想。
2.安全式组合模式
安全式组合模式是为了解决透明式组合模式违反最小知道原则而设计的,其中心思想是顶层的父类Component只实现和本业务相关的方法,将组合逻辑相关的方法(addChild、removeChild等)放到子类中去实现,这样在父类中就不会有组合相关的方法,叶子结点不敢直组合节点的逻辑。
安全式组合模式的实现类图
安全式组合逻辑的代码示例
- 创建顶层抽象父类Component,只实现业务相关的逻辑
public abstract class Component {
protected String name;
public Component(String name){
this.name = name;
}
public abstract void print(String prefix);
}
- 叶子节点继承Component类,并重写相关的方法
public class Leaf extends Component{
public Leaf(String name) {
super(name);
}
@Override
public void print(String prefix) {
System.out.println(prefix + " -" + this.name);
}
}
- 组合对象继承Component,除了重写相关的方法外,还增加组合对象的逻辑
public class Composite extends Component {
private List<Component> children = new ArrayList<>();
public Composite(String name) {
super(name);
}
public void addChild(Component component) {
children.add(component);
}
public void removeChild(Component component) {
children.remove(component);
}
@Override
public void print(String prefix) {
if (children.size() == 0) {
System.out.println(prefix + " -" + this.name);
} else {
System.out.println(prefix + " +" + this.name);
for (Component child : children) {
child.print(prefix + " ");
}
}
}
}
- 编写测试类
public class Client {
@Test
public void testSafeComposite(){
Composite root = new Composite("root");
root.addChild(new Composite("empty Composite"));
root.addChild(new Leaf("level 1 leaf"));
Composite secondComposite = new Composite("second Composite");
secondComposite.addChild(new Leaf("second leaf"));
root.addChild(secondComposite);
root.print("");
}
}
测试结果如下:
+root
-empty Composite
-level 1 leaf
+second Composite
-second leaf
安全式组合模式总结
安全式的组合逻辑将组合相关的方法放到了组合对象Composite中,让整个设计满足了最小知道原则,但是在创建对象的时候,我们就需要区别是Composite对象还是Leaf对象,因为Leaf对象没有相关的addChild和removeChild方法,如果不区分,就会出现无法给Composite对象添加子节点的情况,就像两个测试类中显示的创建second Composite逻辑,在透明式写法中可以直接是
Component secondComposite = new Composite("second Composite");
但是在安全式写法中,其就必须声明成Composite对象:
Composite secondComposite = new Composite("second Composite");
在显示的生活中,安全式的写法相对见得比较多一点,比如windows和linux的文件系统,就是文件夹和文件是分开的类型,新建的时候也是单独创建的文件夹或者文件,有兴趣的小伙伴可以简单写一下相关的示例。
后记
个人总结,欢迎转载、评论、批评指正