兄弟们,老铁们.又到了学习锻炼我们可爱小小大脑的时候了~~~~~
喝了这碗鸡血,学就完了~~~
你穿什么,用什么,开什么,住什么,什么都不算。
你读什么,看什么,想什么,做什么,什么都重要。
1.组合模式定义
将对象以树形结构组织起来,以达成"部分-整体"的层次结构,使客户端对单个对象和组合对象的使用具有一致性.希望用户忽略组合对象与单个对象的不同,统一的使用组合结构中的对象.
树形结构就是组合模式的体现,例如文件夹中可以包含文件和子文件夹,由于使用时,如某个文件夹的方法被调用,由于这会遍历整个树形结构,寻找也包含这个方法的具体实现,其中会用到递归调用的机制对整个结构进行处理,由于文件和文件夹的功能上的区别,在代码中需要去分别对待,这会让程序变得负责.
so,组合模式出生了,让文件夹和文件作为相同的对象来处理.
2.组合模式结构
- 抽象构件角色(Component):它为组合中的对象声明接口,可可以喂共有接口实现缺省行为.
- 叶构造件角色(Leaf):在组合中表示叶节点对象-没有子节点,实现抽象构件角色声明的接口.
- 枝构件角色(Composite):在组合中表示分支节点对象-有子节点,实现抽象构件角色声明的接口:存储子部件.
从客户端程序来说,Leaf和Compostie都是一样的,客户仅知道Compoent这个抽象类,并且Composite类中还持有Compoent抽象类的引用,所以Composite中可以包含任何Compoent抽象类的子类.
3.组合模式实现
先举一个最简单的例子
Component
//容器抽象类,定义行为
public abstract class Component {
//添加
public abstract void addComposite(Component component);
//移除
public abstract void removeComposite(Component component);
//获取子容器
public abstract Component getComposite(int i);
//业务
public abstract void operation();
}
Leaf
public class Leaf extends Component{
//叶子节点关键点在于业务
@Override
public void addComposite(Component component) {
System.out.print("不是子容器");
}
@Override
public void removeComposite(Component component) {
System.out.print("不是子容器");
}
@Override
public Component getComposite(int i) {
System.out.print("不是子容器");
return null;
}
@Override
public void operation() {
System.out.print("业务");
}
}
Composite
public class Composite extends Component{
//存储集合
private List<Component> componentList = new ArrayList<Component>();
@Override
public void addComposite(Component component) {
componentList.add(component);
}
@Override
public void removeComposite(Component component) {
componentList.remove(component);
}
@Override
public Component getComposite(int i) {
return componentList.get(i);
}
@Override
public void operation() {
componentList.forEach( component -> {
component.operation();
});
}
}
组合模式中必须提供对子对象的管理方法,不然无法完成对子对象的添加删除等操作,但是管理方法是在Component中还是在Composite中值得思考,通过Component声明方法是为了使客户看来接口层次上叶和分支没有区别,保证了透明性,但是叶是不存在子类的,因此Component声明的一些方法对叶并不适用,会带来一些安全性问题.而在Composite中声明方法避免了上一种方式的安全性,但是由于叶和分支有不同的接口,又失去了透明性.
<<设计模式>>中认为:在组合模式中,相对于安全性,更强调透明性.所以使用第一种方式在叶中不需要的方法使用空处理或者异常等方式来解决.
下面来一个正式一点的例子.
给一棵小树进行杀虫
抽象构建
public abstract class AbstractTree {
public abstract void add(AbstractTree af);
public abstract void remove(AbstractTree af);
public abstract AbstractTree get(int i);
public abstract void killInsect();
}
树叶
public class TreeLeaf extends AbstractTree {
private int leftNum;
public TreeLeaf(int i){
this.leftNum = i;
}
@Override
public void add(AbstractTree af) {
System.out.println("不支持方法");
}
@Override
public void remove(AbstractTree af) {
System.out.println("不支持方法");
}
@Override
public AbstractTree get(int i) {
System.out.println("不支持方法");
return null;
}
@Override
public void killInsect() {
System.out.println("开始对第--" + leftNum + "--片叶子开始杀虫");
}
}
树枝
public class TreeBranches extends AbstractTree {
private List<AbstractTree> treeList = new ArrayList<>();
private int branchesNum;
public TreeBranches(int i){
this.branchesNum = i;
}
@Override
public void add(AbstractTree af) {
treeList.add(af);
}
@Override
public void remove(AbstractTree af) {
treeList.remove(af);
}
@Override
public AbstractTree get(int i) {
return treeList.get(i);
}
@Override
public void killInsect() {
System.out.println("开始对第※ "+ branchesNum +" ※根树枝进行杀虫");
if (!treeList.isEmpty()){
treeList.forEach(tree -> {
tree.killInsect();
});
}
}
}
测试
public class TreeMain {
public static void main(String[] args) {
TreeBranches branches1 = new TreeBranches(1);
TreeBranches branches2 = new TreeBranches(2);
for (int i = 1;i<10;i++){
TreeLeaf leaf = new TreeLeaf(i);
branches1.add(leaf);
}
for (int j = 1;j < 5;j++){
TreeLeaf treeLeaf = new TreeLeaf(j);
branches2.add(treeLeaf);
}
branches1.add(branches2);
branches1.killInsect();
}
}
4.组合模式优缺点
优点:
- 可以清楚地定义分层次的复杂类型,表示对象的全部层次或者部分层次 ,它让客户端忽略了层次的差异,方便对整个层次经行控制。
- 客户端可以一致的使用一个组合模式或对单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端的代码。
- 在组合模式种增加新的容器构件和叶子构件都很方便,无需对现有类库进行任何修改,符合开闭原则。
- 为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合可以形成复杂的树形机构,但对树形结构的控制却很简单。
缺点:
在增加新的构件时就比较难咯。而且难以限定,有时候希望在一个容器种能有某些特定的对象,例如在某个文件夹只能有image或者gif等。这个就比较难以实现。
5.适用场景
-
在具有整体和部分的层次结构种希望通过一种忽略整体与个体之间差异的,客户端一致对待的情况。
-
在一个使用面向对象语言开发的系统中需要处理一个树形结构的。
-
在一个系统中能分离出叶子和容器的,而且他们的类型还固定不变,需要增加一些新的类型