2、18组合模式
定义
组合模式也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系,其定义如下:将对象组合成树形结构以表示”部分-整体”的层次结构,(统一处理)使得用户对单个对象和组合对象的使用具有一致性(忽略对象组合和个别对象之间的差别)。
组合模式有两种不同的实现:透明模式和安全模式。
安全模式
类图
1)Component抽象构件角色
定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。
2)Leaf叶子构件
叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。
3)Composite树枝构件
树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。
这个构件/角色定义构件的行为,而这样的构件具有子节点。
案例
import java.util.ArrayList;
/**
* 场景类
* TODO 安全模式的组合模式,把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法.
* TODO 下述树叶和树枝的定义直接使用了实现类,这在面向接口编程中很不恰当,与依赖倒置原则冲突,限制了接口的影响范围.
*/
public class Client1 {
public static void main(String[] args) {
//创建一个根节点
Composite root = new Composite();
root.doSomething();
//创建一个树枝构件
Composite branch = new Composite();
//创建一个叶子节点
Leaf leaf = new Leaf();
//建立整体
root.add(branch);
branch.add(leaf);
display(root);
}
//通过递归遍历树
public static void display(Composite root){
for(Component component : root.getChildren()){
if(component instanceof Leaf){
component.doSomething();
}else{
display((Composite)component);
}
}
}
}
/**
* 抽象构件
*/
abstract class Component{
//个体和整体都具有的共享
public void doSomething(){
//业务逻辑
System.out.println("....");
}
}
/**
* 树枝构件
* TODO 组合模式的重点就在树枝结构
*/
class Composite extends Component{
//构件容器
private ArrayList<Component> componentArrayList = new ArrayList<>();
//增加一个叶子节点或树枝构件
public void add(Component component){
this.componentArrayList.add(component);
}
//删除一个叶子节点或树枝构件
public void remove(Component component){
this.componentArrayList.remove(component);
}
//获得分支下的所有叶子构件和树枝构件
public ArrayList<Component> getChildren(){
return this.componentArrayList;
}
}
/**
* 树叶构件
* TODO 树叶节点是没有子下级对象的对象,定义参加组合的原始对象行为.
*/
class Leaf extends Component{
//可以重写父类方法
/*@Override
public void doSomething() {
super.doSomething();
}*/
}
透明模式
类图
案例
import java.util.ArrayList;
/**
* 场景类
* TODO 透明模式:把用来组合使用的方法放到抽象类中.不管是叶子对象还是树枝对象都有相同的结构,如果处理不当,那么会在运行期间出现问题.
* TODO 仅仅在遍历时不用进行牵制的类型转换了,其他的组装跟"安全模式"相比没有任何变化.好处是基本遵循了依赖倒置原则,方便系统进行扩展.
*/
public class Client2 {
public static void main(String[] args) {
//创建一个根节点
Composite root = new Composite();
root.doSomething();
//创建一个树枝构件
Composite branch = new Composite();
//创建一个叶子节点
Leaf leaf = new Leaf();
//建立整体
root.add(branch);
branch.add(leaf);
display(root);
}
//通过递归遍历树
public static void display(Component root){
for(Component component : root.getChildren()){
//叶子节点
if(component instanceof Leaf){
component.doSomething();
}
//树枝节点
else{
display(component);
}
}
}
}
/**
* 抽象构件定义了树枝节点和树叶节点都必须具有的方法和属性
*/
abstract class Component{
//个体和整体都有的共享
public void doSomething(){
//编写业务逻辑
System.out.println("----");
}
//增加一个叶子构件或树枝构件
public abstract void add(Component component);
//删除一个叶子构件或树枝构件
public abstract void remove(Component component);
//获得分支下的所有叶子构件和树枝构件
public abstract ArrayList<Component> getChildren();
}
/**
* 树枝构件
* TODO 组合模式的重点就在树枝结构
*/
class Composite extends Component{
//构件容器
private ArrayList<Component> componentArrayList = new ArrayList<>();
//增加一个叶子节点或树枝构件
@Override
public void add(Component component){
this.componentArrayList.add(component);
}
//删除一个叶子节点或树枝构件
@Override
public void remove(Component component){
this.componentArrayList.remove(component);
}
//获得分支下的所有叶子构件和树枝构件
@Override
public ArrayList<Component> getChildren(){
return this.componentArrayList;
}
}
/**
* 树叶节点
* TODO 空方法直接抛"不支持请求"的异常即可
* TODO @Deprecated注解就是在编译期间高速调用者,在调用此方法时会出现错误.在运行期间也会抛出异常,"该方法已经失效".
*/
class Leaf extends Component{
@Deprecated
@Override
public void add(Component component) {
throw new UnsupportedOperationException();
}
@Deprecated
@Override
public void remove(Component component) {
throw new UnsupportedOperationException();
}
@Deprecated
@Override
public ArrayList<Component> getChildren() {
throw new UnsupportedOperationException();
}
}
遍历
类图
案例
import java.util.ArrayList;
/**
* 场景类
*/
public class Client3 {
public static void main(String[] args) {
}
}
/**
* 抽象构件
*/
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() {
return "Corp{" +
"name='" + name + '\'' +
", position='" + position + '\'' +
", salary=" + salary +
'}';
}
//设置父节点
protected void setParent(Corp _parent){
this.parent = _parent;
}
//得到父节点
public Corp getParent(){
return this.parent;
}
}
class Branch extends Corp{
//下属列表
private ArrayList<Corp> list = new ArrayList<>();
//构造函数是必须的
public Branch(String name, String position, int salary) {
super(name, position, salary);
}
//增加一个下属
public void add(Corp corp){
//设置父节点
this.setParent(this);
this.list.add(corp);
}
//下属列表
public ArrayList<Corp> getList() {
return this.list;
}
}
组合迭代器
类图
案例
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
/**
* 场景类
*/
public class Client4 {
public static void main(String[] args) {
}
}
/**
* 抽象构件(多余的方法略)
*/
abstract class MenuComponent{
protected abstract Iterator createIterator();
}
class Menu extends MenuComponent{
private ArrayList<MenuComponent> menuComponents = new ArrayList<>();
@Override
public Iterator createIterator(){
return new CompositeIterator(menuComponents.iterator());
}
}
class MenuItem extends MenuComponent{
@Override
public Iterator createIterator(){
return new NullIterator();
}
}
/**
* 外部迭代器,所以有许多需要追踪的事情.
*/
class CompositeIterator implements Iterator{
private Stack stack = new Stack();
public CompositeIterator(Iterator iterator) {
this.stack.push(iterator);
}
@Override
public Object next() {
if(hasNext()){
Iterator iterator = (Iterator)stack.peek();
//如果还有下一个元素,那么从堆栈中取出目前的迭代器,然后取得它的下一个元素.
MenuComponent component = (MenuComponent)iterator.next();
if(component instanceof Menu){
//丢进堆栈中
stack.push(component.createIterator());
}
return component;
}
return null;
}
@Override
public boolean hasNext() {
if(stack.isEmpty()){
return false;
}else{
//检查堆栈是否被清空
Iterator iterator = (Iterator)stack.peek();
if(!iterator.hasNext()){
//从堆栈的顶层中取出迭代器
stack.pop();
return hasNext();
}else{
return true;
}
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* 空迭代器
* TODO 这是空对象"设计模式"的另一个例子.
*/
class NullIterator implements Iterator{
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
return null;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
特性
1)高层模块调用简单
一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
2)节点自由增加
使用了组合模式后,如果想增加一个树枝节点、树叶节点都很容易,只要找到它的父节点就成,非常容易扩展,符合开闭原则,对以后的维护非常有利。
使用场景
1)维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
上述例子中是组合模式的真实引用,它依靠了关系数据库的非对象存储功能,非常方便地保存了一个树形结构。
2)从一个整体中能够独立出部分模块或功能的场景。
3)现在的页面结构:上面系统LOGO、下面两部分,左边是导航菜单、右边是展示区。
4)XML,用JDOM或DOM4J来解析。
5)如果组合结构很复杂,或者遍历的代价太高,那么实现组合节点的缓存就很有帮助。比如说,如果要不断地遍历一个组合,而且它的每一个子节点都需要进行某些计算,那就应该使用缓存来临时保存结果,省去遍历的开支。
注意事项
1)只要是树形结构,就要考虑使用组合模式。只要是体现局部和整体的关系的时候,而且这种关系还可能比较深,就要考虑使用组合模式。
2)当发现需求中是体现部分与整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。
3)组合模式定义了包含基本对象和组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
4)组合模式让客户可以一致地使用组合结构和单个对象,二者对于外界来说没有区别,它们具备完全一致的行为接口。
5)在实现组合模式时,有许多设计上的折中。要根据需要平衡透明性和安全性。
6)应该努力让一个类只有一个责任。
标题:设计模式(十八)结构型模式(对象结构型模式)组合/组成模式-COMPOSITE
作者:yazong
地址:https://blog.llyweb.com/articles/2020/08/17/1597596584843.html