1.前言
组合是一种整体与部分的关系,即对象与其内部对象之间的关系。通过之前的外观模式,体会到对象内部是可以很复杂的。最常见的情况便是,一个对象由多个对象组合而成,而部分参与组合的对象又是由多个对象组合而成,很像是树状结构。
这种结构的对象该如何访问呢?作为开发人员肯定想忽略掉这些区别,以一种统一的方式来处理每个对象。
2.概念
组合模式将对象组合成树形结构以表示“整体-部分”的层次结构,使得用户对组合对象和单个对象的使用具有一致性。这种统一的风格便是抽象的体现,需通过抽象类或接口来定义,由各种对象去实现。
这种模式特别适合模块化的结构,即层级结构明显,功能划分细致,像公司的组织架构,又或者计算机的文件系统。
3.场景
一个大的集团公司通常都是由许多子公司组成,每个子公司都有自己的业务范围。但只要是公司,必然有不同的部门,每个部门都有自己的工作目标。只不过,总公司统筹所有子公司而已。
4.写法
// 1.定义访问所有对象的共有接口
public abstract class Function {
protected String name;
// 2.实现缺省行为
public Function(String name) {
this.name = name;
}
public abstract void work();
}
// 1.实现组合对象及共有行为
public class Company extends Function {
private List<Function> mFunctions = new ArrayList<>();
public Company(String name) {
super(name);
}
@Override
public void work() {
System.out.println("公司名为 " + name);
if (mFunctions != null && !mFunctions.isEmpty()) {
for (Function function : mFunctions) {
function.work();
}
}
}
// 2.组合对象的特有行为
public void addFunction(Function function) {
mFunctions.add(function);
}
public void removeFunction(Function function) {
mFunctions.remove(function);
}
public Function getFunction(int index) {
return mFunctions.get(index);
}
}
// 1.实现单个对象及共有行为
public class Department extends Function {
public Department(String name) {
super(name);
}
@Override
public void work() {
System.out.println("部门名为 " + name);
}
// 2.可添加单个对象的特有行为
}
public class Client {
public static void main(String[] args) {
// 1.构造集团公司
Company group = new Company("XXX集团");
Company company = new Company("XXX公司");
Department onlineShopping = new Department("网络购物事业部");
Department mobile = new Department("移动事业部");
group.addFunction(company);
group.addFunction(onlineShopping);
group.addFunction(mobile);
// 2.构造子公司
Company subcompany = new Company("XXX子公司");
Department localLife = new Department("本地生活事业部");
Department cloud = new Department("云产品事业部");
company.addFunction(subcompany);
company.addFunction(localLife);
company.addFunction(cloud);
group.work();
}
}
通过上面的代码,细心的朋友肯定会发现一个问题,那就是违背了依赖倒置原则。在Client类中直接使用Function的具体实现类,增加了代码间的耦合度,还忽视了Function的抽象作用。目前主流的开发方式就是面向接口编程,除了把焦点放在接口的设计外,还应注重接口的使用。
Function类无法使用,关键是因为只声明了公司和部门的共有方法,在实现类中才添加特有方法,对调用者屏蔽了具体实现的细节,这是安全的组合模式。若希望Function类得到使用,可以让其包括所有特有方法的声明,对外公开完整的访问结构,称为透明的组合模式。
public abstract class Function {
protected String name;
public Function(String name) {
this.name = name;
}
public abstract void work();
// 添加Company特有方法的声明
public abstract void addFunction(Function function);
public abstract void removeFunction(Function function);
public abstract Function getFunction(int index);
}
public class Department extends Function {
public Department(String name) {
super(name);
}
@Override
public void work() {
System.out.println("部门名为 " + name);
}
// 1.实现Company特有方法
@Override
public void addFunction(Function function) {
throw new UnsupportedOperationException("No function");
}
@Override
public void removeFunction(Function function) {
throw new UnsupportedOperationException("No function");
}
@Override
public Function getFunction(int index) {
throw new UnsupportedOperationException("No function");
}
// 2.可添加单个对象的特有行为
}
5.总结
在实际开发中,尤其是安卓开发,SDK提供的UI组件架构设计就是使用组合模式。大家可以回顾一下,TextView和ViewGroup继承自View,而共有方法包括了onMeasure()
、onLayout()
和onDraw()
等。之所以这样,除了看中组合模式便于访问结构树中的节点外,还利于增删节点,为树形结构的面向对象实现提供灵活的解决方案。