目录
组合模式的定义
组合模式(Composite Pattern),又叫部分整体模式(Part-Whole Pattern),属于结构型模式的一种,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,是一种对象结构型模式。
该模式创建了一个包含自己对象组的树形结构,其提供了修改相同对象组的方式,使得用户对单个对象和组合对象的使用具有一致性。其核心思想是:将多个对象组合成树形结构,以此结构来表示“整体-部分”之间的层次关系。
讲解组合模式之前先带大家熟悉一下“树形结构”。相信大家对“树形结构”不会太陌生,若刚好不太了解的话可以想一下大树,一颗树它分别有树根、树枝、树叶,正好树形结构中也有根节点、子节点(非叶子节点)、叶子节点。
对于树形结构,当容器对象(如文件夹)的某一个方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员对象(可以是容器对象,也可以是叶子对象)并调用执行。这是靠递归调用的机制实现的。
由于容器对象和叶子对象在功能上的区别,在使用这些对象的代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下我们希望一致地处理它们,因为对于这些对象的区别对待将会使得程序非常复杂。组合模式为解决此类问题而诞生,它可以让叶子对象和容器对象的使用具有一致性。
组合模式的实现
组合模式角色
- 构建角色(Component):可以是接口或抽象类,定义了公共的操作方法,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。管理和访问component子部件的抽象对象,在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
- 叶子角色(Leaf):在组合模式中表示叶子结点对象,叶子节点没有子节点,实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
- 容器角色(Composite):在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
组合模式分为透明式的组合模式和安全式的组合模式
透明式的组合模式
由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
透明式的组合模式类图
透明式的组合模式代码实现
构建角色
package com.common.demo.pattern.composite1;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 抽象构件角色
* @date 2023/07/14 18:04:31
*/
public interface Component {
/**
* 业务方法
*/
void operation();
/**
* 业务方法
*/
void add(Component c);
/**
* 业务方法
*/
void remove(Component c) ;
/**
* 业务方法
*/
Component getChild(int i) ;
}
叶子角色
package com.common.demo.pattern.composite1;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 叶子构件
* @date 2023/07/14 18:08:10
*/
public class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("叶子节点" + name + ":被访问!");
}
@Override
public void add(Component c) {
}
@Override
public void remove(Component c) {
}
@Override
public Component getChild(int i) {
return null;
}
}
容器角色
package com.common.demo.pattern.composite1;
import java.util.ArrayList;
import java.util.List;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 容器构件
* @date 2023/07/14 18:08:43
*/
public class Composite implements Component {
private List<Component> list = new ArrayList<Component>();
@Override
public void add(Component c){
list.add(c);
}
@Override
public void remove(Component c) {
list.remove(c);
}
@Override
public Component getChild(int i) {
return list.get(i);
}
@Override
public void operation(){
for (Component child: list){
child.operation();
}
}
}
测试类
package com.common.demo.pattern.composite1;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc
* @date 2023/07/14 18:10:27
*/
public class Test {
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
测试截图
安全式的组合模式
将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
安全式的组合模式代码类图
安全式的组合模式代码实现
构建角色
package com.common.demo.pattern.composite;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 抽象构件角色
* @date 2023/07/14 18:04:31
*/
public interface Component {
/**
* 业务方法
*/
void operation();
}
叶子角色
package com.common.demo.pattern.composite;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 叶子构件
* @date 2023/07/14 18:08:10
*/
public class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("叶子节点" + name + ":被访问!");
}
}
容器角色
package com.common.demo.pattern.composite;
import java.util.ArrayList;
import java.util.List;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc 容器构件
* @date 2023/07/14 18:08:43
*/
public class Composite implements Component {
private List<Component> list = new ArrayList<Component>();
public void add(Component c){
list.add(c);
}
public void remove(Component c) {
list.remove(c);
}
public Component getChild(int i) {
return list.get(i);
}
@Override
public void operation(){
for (Component child: list){
child.operation();
}
}
}
测试类
package com.common.demo.pattern.composite;
/**
* @author Evan Walker 昂焱数据: https://www.ayshuju.com
* @version 1.0
* @desc
* @date 2023/07/14 18:10:27
*/
public class Test {
public static void main(String[] args) {
Composite c0 = new Composite();
Composite c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
测试截图
组合模式上述两种设计方式可以理解,设计模式并不应该是生套似的,各种设计原则也并不是说一定不能破坏的,所有的种种都是为了更好的解决问题,更好的进行扩展维护,当适度破坏既定的原则,却可以更好的解决问题时,显然这里以单一设计原则换取了透明性,这种折中方案是可取的。
组合模式使用面向对象的思想来实现树形结构的构建与处理,描述了如何将容器对象和叶子对象进行递归组合,实现简单,灵活性好。在树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
组合模式的特点
优点
- 分层控制性:组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 抽象性:客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
- 符合开闭原则:在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
- 控制简单:组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
缺点
- 实现过程较为复杂:在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
- 限制组合内部类型:组合中的所有对象都必须实现同一接口或继承自同一抽象类,这可能会限制特定类型的内部实现。
注意事项
- 在设计组合对象时,需要考虑对子对象的一致性要求,确保子对象能够按照组合模式的要求进行操作。
- 注意避免循环引用问题,防止在组合结构中出现无限循环的情况。
应用场景
- 当需要表示对象的层次结构,并且希望对单个对象和组合对象进行一致处理时,例如树形菜单、文件系统等。
- 当希望将对象组织成树状结构以表示"部分-整体"关系,并且能够以递归方式处理对象时。
- 当希望客户端对组合对象和叶子对象执行相同的操作时。
实际应用:
- 操作系统文件系统:文件和目录可以组成一个树形结构,并以统一的方式进行操作。
- 图形用户界面中的控件层次结构:窗口、面板、按钮等各种控件可以组合成一个复杂的界面结构。
- 组织架构图:公司的各个部门、岗位可以组成一个组织架构图,以表示部门间的层次关系。
总结
- 使用组合模式可以让用户可以使用统一的方式处理整个树形结构的个别对象和组合对象,从而简化客户端的操作。
- 组合模式具有较强的扩展性,当我们想要更改组合对象时,只需要调整内部的层次关系即可,客户端不需要作出任何改动。
- 客户端不用考虑组合中的细节,通过添加节点和叶子就可以创建出复杂的树形结构。
- 当需要处理的对象是树形结构时可以考虑使用组合模式。
更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)