组合模式

组合模式(composite pattern)也称为“整体-部分(Part-Whole)”模式,允许你将对象组合成树形结构来表现"整体-部分"层次结构,使得客户对单个对象和组合对象使用具有一致性,属于结构性模式。

组合关系和聚合关系的区别:

1、组合关系:比如车和轮胎,也可以认为是全体 - 部分的关系,车报废时,轮胎也跟着报废 。(具有相同的生命周期)

part-of关系,父类拥有子类,子类不能独立于父类存在。

2、聚合关系:班级和学生是一种聚合关系。一个班级,可以有学生,也可以没有学生;反过来,当我们把班级解散,学生仍然存在。(具有不同的生命周期)

has-a关系,父类包含子类,子类可以独立于父类存在。

组合模式一般用来描述整体和部分的关系,它将对象组织到树形结构中,最顶层的节点称为根节点,根节点下面包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。

组合模式_java

组合模式的包含3个角色:

抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认的属性和行为;

树枝节点(Composite):定义了树枝节点的节点,组合树枝节点和叶子节点形成一个树形结构;

叶子类(Leaf):叶子节点的对象,其下再无分支,是系统层次遍历的最小单元。

组合模式在代码的具体的实现上,有两种不同的方式,分别是透明组合模式和安全组合模式。

组合模式应用场景

1、希望客户端可用忽略组合对象和单个对象的差异;

2、对象层次具备整体和部分,呈现树形结构;

在我们的生活中组合模式也非常常见,比如树形菜单,操作系统目录结构,公司组织架构等。

透明组合模式

透明组合模式是把公共方法定义在Component 中,这样做的好处是客户端无需分辨是叶子节点和树枝节点,它们具有一样的接口,UML类图如下:

组合模式_java_02

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类图如下:

组合模式_java_03

修改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、设计变得更加抽象