21.4 组合模式的扩展
21.4.1 真实的组合模式
什么是真实的组合模式?就是你在实际项目中使用的组合模式,而不是仅仅依照书本上学习到的模式,它是“实践出真知”。在我们的例子中,经过精简后,确实是类、接口减少了很多,而且程序也简单很多,但是大家可能还是很迷茫,这个Client程序并没有改变多少呀,非常正确,树的组装是跑不了的,你要知道在项目中使用关系型数据库来存储这些信息,你可以从数据库中直接提取出哪些人要分配到树枝,哪些人要分配到树叶,树枝与树枝、树叶的关系等,这些都是由相关的业务人员维护到数据库中的,通常这里是把数据存放到一张单独的表中,表结构如图21-7所示。
![](https://box.kancloud.cn/2016-08-14_57b00367a2799.jpg)
图21-7 关系数据库中存储的树形结构
这张数据表定义了一个树形结构,我们要做的就是从数据库中把它读取出来,然后展现到前台上,用for循环加上递归就可以完成这个读取。用了数据库后,数据和逻辑已经在表中定义好了,我们直接读取放到树上就可以了,这个还是比较容易做的,大家不妨自己考虑一下。
这才是组合模式的真实引用,它依靠了关系数据库的非对象存储性能,非常方便地保存了一个树形结构。大家可以在项目中考虑采用,想想看现在还有哪个项目不使用关系型数据库呢?
21.4.2 透明的组合模式
组合模式有两种不同的实现:透明模式和安全模式,我们上面讲的就是安全模式,那透明模式是什么样子呢?透明模式的通用类图,如图21-8所示。
![](https://box.kancloud.cn/2016-08-14_57b00367ba833.jpg)
图21-8 透明模式的通用类图
我们与图21-6所示的安全模式类图对比一下就非常清楚了,透明模式是把用来组合使用的方法放到抽象类中,比如add()、remove()以及getChildren等方法(顺便说一下,getChildren一般返回的结果为Iterable的实现类,很多,大家可以看JDK的帮助),不管叶子对象还是树枝对象都有相同的结构,通过判断是getChildren的返回值确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题,不是很建议的方式;安全模式就不同了,它是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全,我们的例子使用了安全模式。
由于透明模式的使用者还是比较多,我们也把它的通用源代码共享出来,首先看抽象构件,如代码清单21-22所示。
代码清单21-22 抽象构件
public abstract class Component {
//个体和整体都具有的共享
public void doSomething(){
//编写业务逻辑
}
//增加一个叶子构件或树枝构件
public abstract void add(Component component);
//删除一个叶子构件或树枝构件
public abstract void remove(Component component);
//获得分支下的所有叶子构件和树枝构件
public abstract ArrayList<Component> getChildren();
}
抽象构件定义了树枝节点和树叶节点都必须具有的方法和属性,这样树枝节点的实现就不需要任何变化,如代码清单21-19所示。
树叶节点继承了Component抽象类,不想让它改变有点难,它必须实现三个抽象方法,怎么办?好办,给个空方法,如代码清单21-23所示。
代码清单21-23 树叶节点
public class Leaf extends Component {
@Deprecated
public void add(Component component) throws UnsupportedOperationException{
//空实现,直接抛弃一个"不支持请求"异常
throw new UnsupportedOperationException();
}
@Deprecated
public void remove(Component component)throws UnsupportedOperationException{
//空实现
throw new UnsupportedOperationException();
}
@Deprecated
public ArrayList<Component> getChildren()throws UnsupportedOperationException{
//空实现
throw new UnsupportedOperationException();
}
}
为什么要加个Deprecated注解呢?就是在编译器期告诉调用者,你可以调我这个方法,但是可能出现错误哦,我已经告诉你“该方法已经失效”了,你还使用那在运行期也会抛出UnsupportedOperationException异常。
在透明模式下,遍历整个树形结构是比较容易的,不用进行强制类型转换,如代码清单21-24所示。
代码清单21-24 树结构遍历
public class Client {
//通过递归遍历树
public static void display(Component root){
for(Component c:root.getChildren()){
if(c instanceof Leaf){ //叶子节点
c.doSomething();
}else{ //树枝节点
display(c);
}
}
}
}
仅仅在遍历时不再进行牵制的类型转化了,其他的组装则没有任何变化。透明模式的好处就是它基本遵循了依赖倒转原则,方便系统进行扩展。
21.4.3 组合模式的遍历
我们在上面也还提到了一个问题,就是树的遍历问题,从上到下遍历没有问题,但是我要是从下往上遍历呢?比如组织机构这棵树,我从中抽取一个用户,要找到它的上级有哪些,下级有哪些,怎么处理?想想,再想想!想出来了吧,我们对下答案,类图如图21-9所示。
![](https://box.kancloud.cn/2016-08-14_57b00367ce4fc.jpg)
图21-9 增加父查询的类图
看类图中,在Corp类中增加了两个方法,setParent是设置父节点是谁,getParent是查找父节点是谁,我们来看一下程序的改变,如代码清单21-25所示。
代码清单21-25 抽象构件
public abstract class Corp {
//公司每个人都有名称
private String name = "";
//公司每个人都职位
private String position = "";
//公司每个人都有薪水
private int salary =0;
//父节点是谁
private Corp parent = null;
public Corp(String _name,String _position,int _salary){
this.name = _name;
this.position = _position;
this.salary = _salary;
}
//获得员工信息
public String getInfo(){
String info = "";
info = "姓名:" + this.name;
info = info + "\t职位:"+ this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
//设置父节点
protected void setParent(Corp _parent){
this.parent = _parent;
}
//得到父节点
public Corp getParent(){
return this.parent;
}
}
就增加了粗体部分,然后我们再来看看树枝节点的改变,如代码清单21-26所示。
代码清单21-26 树枝构件
public class Branch extends Corp {
//领导下边有哪些下级领导和小兵
ArrayList<Corp> subordinateList = new ArrayList<Corp>();
//构造函数是必需的
public Branch(String _name,String _position,int _salary){
super(_name,_position,_salary);
}
//增加一个下属,可能是小头目,也可能是个小兵
public void addSubordinate(Corp corp) {
corp.setParent(this); //设置父节点
this.subordinateList.add(corp);
}
//我有哪些下属
public ArrayList<Corp> getSubordinate() {
return this.subordinateList;
}
}
增加了粗体部分。看懂程序了吗?甭管是树枝节点还是树叶节点,在每个节点都增加了一个属性:父节点对象,这样在树枝节点增加子节点或叶子节点是设置父节点,然后你看整棵树除了根节点外每个节点都有一个父节点,剩下的事情还不好处理吗?每个节点上都有父节点了,你要往上找,那就找呗!大家自己考虑一下,写个find方法,然后一步一步往上找,非常简单的方法,这里就不再赘述。
有了这个parent属性,什么后序遍历(从下往上找)、中序遍历(从中间某个环节往上或往下遍历)都解决了,这个就不多说了。
再提一个问题,树叶节点和树枝节点是有顺序的,你不能乱排,怎么办?比如我们上面的例子,研发一组下边有3个成员,这3个成员要进行排序(在机关里这叫做排位,同样是同事也有个先后升迁顺序),你怎么处理?问我呀,问你呢,好好想想,以后用得着的!
- 前言
- 第一部分 大旗不挥,谁敢冲锋——6大设计原则全新解读
- 第1章 单一职责原则
- 1.2 绝杀技,打破你的传统思维
- 1.3 我单纯,所以我快乐
- 1.4 最佳实践
- 第2章 里氏替换原则
- 2.2 纠纷不断,规则压制
- 2.3 最佳实践
- 第3章 依赖倒置原则
- 3.2 言而无信,你太需要契约
- 3.3 依赖的三种写法
- 3.4 最佳实践
- 第4章 接口隔离原则
- 4.2 美女何其多,观点各不同
- 4.3 保证接口的纯洁性
- 4.4 最佳实践
- 第5章 迪米特法则
- 5.2 我的知识你知道得越少越好
- 5.3 最佳实践
- 第6章 开闭原则
- 6.2 开闭原则的庐山真面目
- 6.3 为什么要采用开闭原则
- 6.4 如何使用开闭原则
- 6.5 最佳实践
- 第二部分 真刀实枪 ——23种设计模式完美演绎
- 第7章 单例模式
- 7.2 单例模式的定义
- 7.3 单例模式的应用
- 7.4 单例模式的扩展
- 7.5 最佳实践
- 第8章 工厂方法模式
- 8.2 工厂方法模式的定义
- 8.3 工厂方法模式的应用
- 8.4 工厂方法模式的扩展
- 8.5 最佳实践
- 第9章 抽象工厂模式
- 9.2 抽象工厂模式的定义
- 9.3 抽象工厂模式的应用
- 9.4 最佳实践
- 第10章 模板方法模式
- 10.2 模板方法模式的定义
- 10.3 模板方法模式的应用
- 10.4 模板方法模式的扩展
- 10.5 最佳实践
- 第11章 建造者模式
- 11.2 建造者模式的定义
- 11.3 建造者模式的应用
- 11.4 建造者模式的扩展
- 11.5 最佳实践
- 第12章 代理模式
- 12.2 代理模式的定义
- 12.3 代理模式的应用
- 12.4 代理模式的扩展
- 12.5 最佳实践
- 第13章 原型模式
- 13.2 原型模式的定义
- 13.3 原型模式的应用
- 13.4 原型模式的注意事项
- 13.5 最佳实践
- 第14章 中介者模式
- 14.2 中介者模式的定义
- 14.3 中介者模式的应用
- 14.4 中介者模式的实际应用
- 14.5 最佳实践
- 第15章 命令模式
- 15.2 命令模式的定义
- 15.3 命令模式的应用
- 15.4 命令模式的扩展
- 15.5 最佳实践
- 第16章 责任链模式
- 16.2 责任链模式的定义
- 16.3 责任链模式的应用
- 16.4 最佳实践
- 第17章 装饰模式
- 17.2 装饰模式的定义
- 17.3 装饰模式应用
- 17.4 最佳实践
- 第18章 策略模式
- 18.2 策略模式的定义
- 18.3 策略模式的应用
- 18.4 策略模式的扩展
- 18.5 最佳实践
- 第19章 适配器模式
- 19.2 适配器模式的定义
- 19.3 适配器模式的应用
- 19.4 适配器模式的扩展
- 19.5 最佳实践
- 第20章 迭代器模式
- 20.2 迭代器模式的定义
- 20.3 迭代器模式的应用
- 20.4 最佳实践
- 第21章 组合模式
- 21.2 组合模式的定义
- 21.3 组合模式的应用
- 21.4 组合模式的扩展
- 21.5 最佳实践
- 第22章 观察者模式
- 22.2 观察者模式的定义
- 22.3 观察者模式的应用
- 22.4 观察者模式的扩展
- 22.5 最佳实践
- 第23章 门面模式
- 23.2 门面模式的定义
- 23.3 门面模式的应用
- 23.4 门面模式的注意事项
- 23.5 最佳实践
- 第24章 备忘录模式
- 24.2 备忘录模式的定义
- 24.3 备忘录模式的应用
- 24.4 备忘录模式的扩展
- 24.5 最佳实践
- 第25章 访问者模式
- 25.2 访问者模式的定义
- 25.3 访问者模式的应用
- 25.4 访问者模式的扩展
- 25.5 最佳实践
- 第26章 状态模式
- 26.2 状态模式的定义
- 26.3 状态模式的应用
- 第27章 解释器模式
- 27.2 解释器模式的定义
- 27.3 解释器模式的应用
- 27.4 最佳实践
- 第28章 享元模式
- 28.2 享元模式的定义
- 28.3 享元模式的应用
- 28.4 享元模式的扩展
- 28.5 最佳实践
- 第29章 桥梁模式
- 29.2 桥梁模式的定义
- 29.3 桥梁模式的应用
- 29.4 最佳实践
- 第三部分 谁的地盘谁做主 ——设计模式PK
- 第30章 创建类模式大PK
- 30.1 工厂方法模式VS建造者模式
- 30.2 抽象工厂模式VS建造者模式
- 第31章 结构类模式大PK
- 31.1 代理模式VS装饰模式
- 31.2 装饰模式VS适配器模式
- 第32章 行为类模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS状态模式
- 32.3 观察者模式VS责任链模式
- 第33章 跨战区PK
- 33.1 策略模式VS桥梁模式
- 33.2 门面模式VS中介者模式
- 33.3 包装模式群PK
- 第四部分 完美世界 ——设计模式混编
- 第34章 命令模式+责任链模式
- 34.2 混编小结
- 第35章 工厂方法模式+策略模式
- 35.2 混编小结
- 第36章 观察者模式+中介者模式
- 36.2 混编小结
- 第五部分 扩展篇
- 第37章 MVC框架
- 37.2 最佳实践
- 第38章 新模式
- 38.1 规格模式
- 38.2 对象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空对象模式
- 附录 23种设计模式彩图