这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战
组合模式
概念
组合模式(Composite Pattern),又叫部分整体模式,主要是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
组合模式一般用来表示整体和部分的关系,将对象组织到树形结构中,最顶层的节点也称为根节点,根节点下面包含子树和叶子节点,子树有包括子树和叶子节点,树形结构如下图所示:
通过上图可以看出,根节点和子树都是同一种数据类型,而叶子节点和子树从定义上来讲不是同一种类型,但是在组合模式中,根节点、子树、叶子节点都共同看成同一种数据类型,或者是同一种接口定义。
应用场景
-
层级结构
-
部门组织结构
-
希望客户端可以忽略组合对象与单个对象的差异
-
对象层次具备整体和部分的关系,呈显树形结构
透明组合模式的写法
组合模式一般具有三种角色,分别是抽象根节点角色、树叶构件角色(叶子节点)、树枝构件角色(树枝节点),概念如下所示:
-
抽象构件角色(Composite):定义系统歌层次对象的共有方法和属性,可以预先定义一些默认行为和属性,是组合中对象声明接口,实现所有类共有接口的默认行为。
-
树枝构件角色(Composite):定义有树枝节点部件的组合部件行为,存储子部件,组合树枝节点和叶子节点形成一个树形结构。主要是在Component接口中实现与子部件有关的操作。
-
树叶构件角色(Leaf):树叶构件角色就是叶子节点,下面没有其他的分支,是系统便利的最小层次。
一般情况下组合模式在代码的具体实现上分为两种,一种是透明组合模式,一直是安全组合模式。本主题主要讲透明组合模式。
透明组合模式就是把所有的公共方法都定义到Composite中,这样就不需要分辨那些事叶子节点,那些是树枝节点,他们具有统一的接口类型。
- 抽象根节点(Composite)
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public abstract String operation();
public boolean addChild(Component component) {
throw new UnsupportedOperationException("addChild not supported!");
}
public boolean removeChild(Component component) {
throw new UnsupportedOperationException("removeChild not supported!");
}
public Component getChild(int index) {
throw new UnsupportedOperationException("getChild not supported!");
}
}
- 树枝构件角色(Composite)
public class Composite extends Component {
private List<Component> mComponents;
public Composite(String name) {
super(name);
this.mComponents = new ArrayList<Component>();
}
@Override
public String operation() {
StringBuilder builder = new StringBuilder(this.name);
for (Component component : this.mComponents) {
builder.append("\n");
builder.append(component.operation());
}
return builder.toString();
}
@Override
public boolean addChild(Component component) {
return this.mComponents.add(component);
}
@Override
public boolean removeChild(Component component) {
return this.mComponents.remove(component);
}
@Override
public Component getChild(int index) {
return this.mComponents.get(index);
}
}
- 树叶构件角色(Leaf)叶子节点
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public String operation() {
return this.name;
}
}
- UML结构图
透明组合模式就是把所有的方法都添加到Component中,这样客户端就不需要知道哪些事叶子节点和树枝节点,但是透明组合模式就违反了设计模式的七大原则之一单一职责原则,即最少知道原则,然后叶子节点也会继承一些他不需要的的方法(管理其他操作的方法),这就违反了设计模式的原则之接口隔离原则。因此还有一种安全的写法就出现了,即安全组合模式。
安全组合模式的写法
安全组合模式是只规定系统各个层次的最基础的一致行为,而把组合本身的方法放到自身当中。
- Component抽象根节点
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public abstract String operation();
}
- Composite(树枝组件)
public class Composite extends Component {
private List<Component> mComponents;
public Composite(String name) {
super(name);
this.mComponents = new ArrayList<Component>();
}
@Override
public String operation() {
StringBuilder builder = new StringBuilder(this.name);
for (Component component : this.mComponents) {
builder.append("\n");
builder.append(component.operation());
}
return builder.toString();
}
public boolean addChild(Component component) {
return this.mComponents.add(component);
}
public boolean removeChild(Component component) {
return this.mComponents.remove(component);
}
public Component getChild(int index) {
return this.mComponents.get(index);
}
}
- Leaf叶子节点
public class Leaf extends Component
public Leaf(String name) {
super(name);
}
@Override
public String operation() {
return this.name;
}
}
- UML结构图
通过上述的代码和UML结构图可以知道,安全组合模式就是让接口定义指责清晰,同时符合设计模式的的单一职责原则和接口隔离原则,虽然安全组合模式符合设计原则,但是也有缺点,主要是客户需要区分树枝节点和叶子节点,才能正确的处理各个层次之间的操作,客户无法依赖抽象,就违背了设计模式的依赖倒置原则。
无论是透明组合模式还是安全组合模式都有各自的优点和缺点,通过自己的业务场景详情选择使用两种组合方法之一。
组合模式的优缺点
优点
-
高层模块(客户端)调用简单。组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码
-
节点自由增加,更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”
缺点
-
在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则
-
设计较复杂,客户端需要花更多时间理清类之间的层次关系
-
不容易限制容器中的构件
-
不容易用继承的方法来增加构件的新功能