组合模式
组合模式属于结构型设计模式.
组合模式又可叫做【部分-整体】模式.它将部分与整体的关系通过树的形式表现出来.分为安全模式与透明模式.
常规的数上会有各种枝干树枝与树叶,树干的每一个分叉点则理解为一个子节点,而树叶则认为是末节点,
将树枝也树叶理解为每一个部分,所有的部分组合起来就是一个整体.
比如城市:
- 四川省--成都市-成都
- 四川省--绵阳市-绵阳
组合模式将使单个具体的对象与整体对象具有一致性.
应用场景
- 从具体的整体模块拆分为具体的子模块
- 保证统一性,将单个对象与组合对象的用法改为一致
- 将对象理解为【部分-整体】
代码示例(安全模式)
(一)接口要做的事
public interface SafeComponent {
public void println(String space);
}
(一)树枝
public class SafeComposite implements SafeComponent {
private String name;
private List<SafeComponent> safeComponents = new ArrayList<>();
public SafeComposite(String name) {
this.name = name;
}
@Override
public void println(String space) {
System.out.println(space + '+' + this.name);
if (this.safeComponents != null) {
space += " ";
for (SafeComponent component : safeComponents) {
component.println(space);
}
}
}
public void addChild(SafeComponent safeComponent) {
safeComponents.add(safeComponent);
}
public void removeChild(SafeComponent safeComponent) {
safeComponents.remove(safeComponent);
}
public void clear() {
safeComponents.clear();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<SafeComponent> getSafeComponents() {
return safeComponents;
}
public void setSafeComponents(List<SafeComponent> safeComponents) {
this.safeComponents = safeComponents;
}
}
树枝作为一个节点,可向下添加删除节点
(一)树叶
public class SafeLeaf implements SafeComponent{
private String name;
public SafeLeaf(String name) {
this.name = name;
}
@Override
public void println(String space) {
System.out.println(space + "-" + name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
树叶作为末节点,不能向下添加节点,只能展示或者做自己能做的事.
调用方式
//安全组合
SafeComposite safeComponent=new SafeComposite("Test");
SafeComposite beijing=new SafeComposite("北京市");
SafeLeaf beijingshi=new SafeLeaf("北京市");
beijing.addChild(beijingshi);
safeComponent.addChild(beijing);
SafeComposite sichuan=new SafeComposite("四川省");
SafeLeaf chengdushi=new SafeLeaf("成都市");
SafeLeaf mianyangshi=new SafeLeaf("绵阳市");
sichuan.addChild(chengdushi);
sichuan.addChild(mianyangshi);
safeComponent.addChild(sichuan);
SafeComposite guangdong=new SafeComposite("广东省");
SafeLeaf guangdongshi=new SafeLeaf("广东市");
guangdong.addChild(guangdongshi);
safeComponent.addChild(guangdong);
safeComponent.println(" ");
显示结果
+全国城市
+北京市
-北京
+四川省
-成都市
-绵阳市
+广东省
-广州市
在上述的显示结果中明显可以看出是一个由大到小的树枝型结构,并且在代码示例中的树枝因为可以向下添加节点,而树叶不能添加,所以树枝和树叶是区分对待他们的方法也存在差异.而这种差异则保证了安全性.
但是因为树枝与树叶具有不同的方法,因此他们显示的不够透明,因此还有一种模式叫做透明组合模式,会将树枝与树叶透明,客户端不必具体区分树枝或者树叶.
代码示例(透明模式)
(一)抽象接口要做的事
public abstract class TransparentComponent {
protected List<TransparentComponent> components = new ArrayList<>();
private String name;
public TransparentComponent(String name) {
this.name = name;
}
public void addChild(TransparentComponent component) {
throw new UnsupportedOperationException("TransparentComponent不支持此方法");
}
public void removeChild(TransparentComponent component) {
throw new UnsupportedOperationException("TransparentComponent不支持此方法");
}
public void clearAll() {
throw new UnsupportedOperationException("TransparentComponent不支持此方法");
}
public List<TransparentComponent> getList() {
throw new UnsupportedOperationException("TransparentComponent不支持此方法");
}
public abstract void println(String space);
public List<TransparentComponent> getComponents() {
return components;
}
public void setComponents(List<TransparentComponent> components) {
this.components = components;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(二)树枝节点
public class TransparentComposite extends TransparentComponent {
public TransparentComposite(String name) {
super(name);
}
public void addChild(TransparentComponent component) {
components.add(component);
}
public void removeChild(TransparentComponent component) {
components.remove(component);
}
public void clearAll() {
components.clear();
}
public List<TransparentComponent> getList() {
return components;
}
@Override
public void println(String space) {
//首先输出自身
System.out.println(space + "+" + getName());
//如果还包含有子节点对象,输出这些对象
if (this.components != null) {
//向后缩进,主要为了打印效果,没有实际意义
space += " ";
for (TransparentComponent component : components) {
component.println(space);
}
}
}
}
(三)树叶节点
public class TransparentLeaf extends TransparentComponent {
public TransparentLeaf(String name) {
super(name);
}
@Override
public void println(String space) {
System.out.println(space + "-" + getName());
}
}
调用方式
//透明组合
TransparentComponent component=new TransparentComposite("全国城市");
TransparentComponent component1=new TransparentComposite("北京市");
TransparentComponent component4=new TransparentLeaf("北京");
component1.addChild(component4);
TransparentComponent component2=new TransparentComposite("四川省");
TransparentComponent component5=new TransparentLeaf("成都市");
TransparentComponent component7=new TransparentLeaf("绵阳市");
component2.addChild(component5);
component2.addChild(component7);
TransparentComponent component3=new TransparentComposite("广东省");
TransparentComponent component6=new TransparentLeaf("广州市");
component3.addChild(component6);
显示结果
+全国城市
+北京市
-北京
+四川省
-成都市
-绵阳市
+广东省
-广州市
透明模式的结果与安全模式的结果如出一辙.
它使用的统一的接口,符合依赖导致原则,面向接口编程.而安全模式中则依赖的是具体的类的方法,违背依赖导致原则.
对于调用的人来说,透明模式只关心具体的接口,具体的节点是树叶还是树枝不关心,是一个统一的操作.
总结
- 优点
- 对象与对象之间是树形结构,结构清晰,容易控制
- 新增子节点的话,不必修改当前类的结构
- 透明模式上更是统一使用具体的方法,不用关心具体的节点
- 缺点
- 设计抽象,需要对业务比较熟悉的情况下进行抽象
在平常开发中建议在重构过程中熟悉业务的情况下载采用组合模式的透明模式,因为在业务使用上树形结构的递归实在太过抽象,并不是所有方法都与子节点具有关系.