组合模式
组合模式(composite pattern)也称为“整体-部分(Part-Whole)”模式,允许你将对象组合成树形结构来表现"整体-部分"层次结构,使得客户对单个对象和组合对象使用具有一致性,属于结构性模式。
组合关系和聚合关系的区别:
1、组合关系:比如车和轮胎,也可以认为是全体 - 部分的关系,车报废时,轮胎也跟着报废 。(具有相同的生命周期)
part-of关系,父类拥有子类,子类不能独立于父类存在。
2、聚合关系:班级和学生是一种聚合关系。一个班级,可以有学生,也可以没有学生;反过来,当我们把班级解散,学生仍然存在。(具有不同的生命周期)
has-a关系,父类包含子类,子类可以独立于父类存在。
组合模式一般用来描述整体和部分的关系,它将对象组织到树形结构中,最顶层的节点称为根节点,根节点下面包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。
组合模式的包含3个角色:
抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认的属性和行为;
树枝节点(Composite):定义了树枝节点的节点,组合树枝节点和叶子节点形成一个树形结构;
叶子类(Leaf):叶子节点的对象,其下再无分支,是系统层次遍历的最小单元。
组合模式在代码的具体的实现上,有两种不同的方式,分别是透明组合模式和安全组合模式。
组合模式应用场景
1、希望客户端可用忽略组合对象和单个对象的差异;
2、对象层次具备整体和部分,呈现树形结构;
在我们的生活中组合模式也非常常见,比如树形菜单,操作系统目录结构,公司组织架构等。
透明组合模式
透明组合模式是把公共方法定义在Component 中,这样做的好处是客户端无需分辨是叶子节点和树枝节点,它们具有一样的接口,UML类图如下:
Component类:
public abstract class Component {
protected String name;
protected int level;
public Component(String name,int level) {
this.name = name;
this.level = level;
}
abstract void print();
abstract public void add(Component component);
abstract public void remove(Component component);
}
Composite类:
public class Composite extends Component {
private List<Component> child;
public Composite(String name, int level) {
super(name, level);
child = new ArrayList<>();
}
@Override
void print() {
for (int i = 0; i < level; i++) {
System.out.print("##");
}
System.out.println("Composite:" + name);
for (Component component : child) {
component.print();
}
}
@Override
public void add(Component component) {
child.add(component);
}
@Override
public void remove(Component component) {
child.remove(component);
}
}
Leaf类:
public class Leaf extends Component {
public Leaf(String name, int level) {
super(name, level);
}
@Override
void print() {
for (int i = 0; i < level; i++) {
System.out.print("##");
}
System.out.println("leaf:" + name);
}
@Override
public void add(Component component) {
// 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点
throw new UnsupportedOperationException();
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException();
}
}
Client类:
public class Client {
public static void main(String[] args) {
Composite root = new Composite("root",0);
Component node1 = new Leaf("1",1);
Component node2 = new Composite("2",1);
Component node3 = new Leaf("3",1);
root.add(node1);
root.add(node2);
root.add(node3);
Component node21 = new Leaf("21",2);
Component node22 = new Composite("22",2);
node2.add(node21);
node2.add(node22);
Component node221 = new Leaf("221",3);
node22.add(node221);
root.print();
}
}
运行结果:
Composite:root##leaf:1##Composite:2####leaf:21####Composite:22######leaf:221##leaf:3
透明组合模式是把所有公共方法都定义在Component中,这样做的好处是客户端无需分辨是叶子节点还是树枝节点,它们是一样的接口;缺点是叶子节点会继承一些用不到的方法,这种方式违背了接口隔离原则。
下面我们来看下安全组合模式的写法。
安全组合模式
安全组合模式只是规定系统各个层次的最基础的一致行为,而把组合(树节点)本身的方法(添加、删除)放大自身当中,UML类图如下:
修改Component类:
public abstract class Component {
protected String name;
protected int level;
public Component(String name,int level) {
this.name = name;
this.level = level;
}
abstract void print();
}
修改Leaf类:
public class Leaf extends Component {
public Leaf(String name, int level) {
super(name, level);
}
@Override
void print() {
for (int i = 0; i < level; i++) {
System.out.print("##");
}
System.out.println("leaf:" + name);
}
}
从上面可以看出,Component减少了添加和删除节点方法,因为叶子节点是不需要的,没有必要实现了。
组合模式的优缺点
优点:
1、清楚定义了分层次的复杂对象,表示对象的全部和部分的层次
2、客户端不必担心层次的差异,方便对整个层次结构的控制
3、简化客户端代码
4、符合开闭原则
缺点:
1、限制类的时候会比较复杂
2、设计变得更加抽象