设计模式-组合模式(Composite)
一、概念
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
二、模式动机
组合模式,通过设计一个抽像的组件类,使它既代表叶子对象,又代表组合对象,将叶子对象和组合对象统一起来。使得客户端在操作时不再区分当前操作的是叶子对象还是组合对象,而是以一个统一方式来操作。
三、模式的结构
示意代码如下:
package composite.structure; /** * 统一叶子和组件的抽象构件类 * @ClassName: Component * @author beteman6988 * @date 2017年11月29日 下午10:48:18 * */ public abstract class Component { /** * 叶子和树技构件都具备的商业逻辑 * @Title: sampleOperation * @param * @return void * @throws */ public abstract void sampleOperation(); /** * 向组合对象中添加组件对象 * @Title: addChild * @param @param child * @return void * @throws */ public abstract void addChild(Component child); /** * 向组合对象中删除组件对象 * @param child * @roseuid 5A1EC291037D */ public abstract void removeChild(Component child) ; /** * 从组合对象中取出第index个位置的组件对象 * @param index * @return Component * @roseuid 5A1EC2F2011B */ public abstract Component getChildren(Integer index) ; }
package composite.structure; import java.util.ArrayList; import java.util.List; /** * 组合对像,通常存储子组件对象,实现抽象组件里面定义的与操作子组件对象有关的行为。 * @ClassName: Composite * @author beteman6988 * @date 2017年12月3日 上午9:22:08 * */ public class Composite extends Component { List<Component> childComponent=new ArrayList<Component>(); /** * 商业逻辑,这里样例迭代所有子组件的该方法 */ @Override public void sampleOperation() { for(Component component:childComponent) { component.sampleOperation(); } } @Override public void addChild(Component child) { this.childComponent.add(child); } @Override public void removeChild(Component child) { this.childComponent.remove(child); } @Override public Component getChildren(Integer index) { if(!childComponent.isEmpty() && childComponent.size()>index ) { return this.childComponent.get(index); } return null; } }
package composite.structure; /** * 叶子组件对象 * @ClassName: Leaf * @author beteman6988 * @date 2017年12月3日 上午9:25:42 * */ public class Leaf extends Component { /** * 商业逻辑 */ @Override public void sampleOperation() { //具体的商业逻辑 } /** * 由于是叶子对象节点,不具备增加组件对象的功能,所以可以提供平庸实现 */ @Override public void addChild(Component child) { } /** * 由于是叶子对象节点,不具备删除组件对象的功能,所以可以提供平庸实现 */ @Override public void removeChild(Component child) { // TODO Auto-generated method stub } /** * 由于是叶子对象节点,不具获取子组件对象的功能,所以可以提供平庸实现 */ @Override public Component getChildren(Integer index) { // TODO Auto-generated method stub return null; } }
角色说明:
Component:抽象的组件对象,可以是一个抽象类或者是一个接口,它既代表叶子对象,也代组合对象,让客户端通过这个接口来管理整个对象结构,上面的样例代码定义的是一个抽象类,且管理组合对象的方法是抽象的,其实也可以提供默认的实现。不提供默认实现的好处就是叶子对象针对这些不支持的行为必须给出明确的处理方式。提供默认形为的好处,如果默认的是针对叶子对象的行为,那么针对叶子对象就不需要对这些不支持的形为提供明确的处理方式。并没有说哪种就好哪种就劣,各有优缺。
Composite:组合对象,通常存储子组件,并包含操作子组件的具体行为,如对子组件进行增删改查。
Leaf:叶子节点组件:定义和实现与叶子对象的行为,不能包含其它子组件。
在组合模式中,对象分为两种,一为Composite对象,它可以包含其它的Composite或叶子对象,就像一个容器,二为不能包含子组件的Leaf对象。对于Composite对像,为了管理子组件对象,就要定以一些管理子组件的操作行为,以上的例 子中将这些操作形为定义在了抽象的Component组件中,但是这些行为其实对于Leaf对象是没有意义的,这些行为可以放在Composite中,而非放在在Component中。所以跟据Composite对象中管理子组件的行为操作是放在Componnet或是Composite中,细分为了“透明模式”和“安全模式”
透明模式:上面的示例结构为透明模式,透明模式对于客户端来说,Leaf对象和Composite对象具有相同的行为,对于客户端而言,Leaf对角和Composite对象是透明的,他们都是Component对象,具有相同的接口行为,也可以在Leaf对象上使用增加子组件的行为,只是这些行为对于Leaf对象是无意义的,这也意为着是不安全的。
安全模式:针对透明模式中,管理子组件的行为如果不定义在Component中,而是定义在Composite中,这样对于Leaf对象就是安全的了。因为客户端对于Leaf对象,就无在调用管理子组件的行为。模式结构如下:
示意代码如下:
package composite.structure.safety; /** * 统一叶子和组件的抽象构件类 * @ClassName: Component * @author beteman6988 * @date 2017年11月29日 下午10:48:18 * */ public abstract class Component { /** * 叶子和树技构件都具备的商业逻辑 * @Title: sampleOperation * @param * @return void * @throws */ public abstract void sampleOperation(); }
package composite.structure.safety; import java.util.ArrayList; import java.util.List; /** * 组合对像,通常存储子组件对象,实现抽象组件里面定义的与操作子组件对象有关的行为。 * @ClassName: Composite * @author beteman6988 * @date 2017年12月3日 上午9:22:08 * */ public class Composite extends Component { List<Component> childComponent=new ArrayList<Component>(); /** * 商业逻辑,这里样例迭代所有子组件的该方法 */ @Override public void sampleOperation() { for(Component component:childComponent) { component.sampleOperation(); } } /** * 向组合对象中添加组件对象 * @Title: addChild * @param @param child * @return void * @throws */ public void addChild(Component child) { this.childComponent.add(child); } /** * 向组合对象中删除组件对象 * @Title: removeChild * @param @param child * @return void * @throws */ public void removeChild(Component child) { this.childComponent.remove(child); } /** * 从组合对象中取出第index个位置的组件对象 * @Title: getChildren * @param @param index * @param @return * @return Component * @throws */ public Component getChildren(Integer index) { if(!childComponent.isEmpty() && childComponent.size()>index ) { return this.childComponent.get(index); } return null; } }
package composite.structure.safety; /** * 叶子组件对象 * @ClassName: Leaf * @author beteman6988 * @date 2017年12月3日 上午9:25:42 * */ public class Leaf extends Component { /** * 商业逻辑 */ @Override public void sampleOperation() { //具体的商业逻辑 } }
四、模式样例
打印出以常见学的学校组织架构为例,如下:
XX大学
信息工程学院
计算机科学系
电子与信息工程系
建筑工程学院
土木建筑系
工程管理系
透明模式:
package composite.sample; /** * 院系抽像接口 * @ClassName: SCCompent * @author beteman6988 * @date 2017年12月9日 下午8:15:46 * */ public interface SCComponent { /** * get院系名称 * @Title: getName * @param @return * @return String * @throws */ public String getName(); /** * 设置院系名称 * @Title: setName * @param @return * @return * @throws */ public void setName(String name); /** * 添加下级院系 * @Title: addSCc * @param @param scComponent * @return void * @throws */ public void addSCc(SCComponent scComponent); /** * 移去下级院系 * @Title: removeSCc * @param @param scComponent * @return void * @throws */ public void removeSCc(SCComponent scComponent); /** * 打印院系名称 * @Title: printName * @param @param preStr * @return void * @throws */ public void printName(String preStr); }
package composite.sample; /** * 叶子系节点 * @ClassName: Department * @author beteman6988 * @date 2017年12月9日 下午8:25:53 * */ public class Department implements SCComponent { private String departmentName; @Override public String getName() { // TODO Auto-generated method stub return departmentName; } @Override public void setName(String name) { this.departmentName=name; } @Override public void addSCc(SCComponent scComponent) { System.out.println("Department不支持addSCc行为!"); } @Override public void removeSCc(SCComponent scComponent) { System.out.println("Department不支持removeSCc行为!"); } @Override public void printName(String preStr) { System.out.println(preStr+this.departmentName); } }
package composite.sample; import java.util.ArrayList; import java.util.List; /** *院系设置的组合对象 * @ClassName: SCComposite * @author beteman6988 * @date 2017年12月9日 下午8:37:44 * */ public class SCComposite implements SCComponent { private String name; private List<SCComponent> sCCList=new ArrayList<SCComponent>(); @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name=name; } @Override public void addSCc(SCComponent scComponent) { if(sCCList.indexOf(scComponent)<0) { sCCList.add(scComponent); } } @Override public void removeSCc(SCComponent scComponent) { this.sCCList.remove(scComponent); } @Override public void printName(String preStr) { System.out.println(preStr+this.name); for(SCComponent component:sCCList) { component.printName(preStr+" "); } } }
package composite.sample; public class Client { public static void main(String[] args) { SCComponent head=new SCComposite(); head.setName("XX大学"); SCComponent sCCOne=new SCComposite(); sCCOne.setName("信息工程学院"); SCComponent sCCTwo=new SCComposite(); sCCTwo.setName("建筑工程学院"); head.addSCc(sCCOne); head.addSCc(sCCTwo); SCComponent dptOne=new Department(); dptOne.setName("计算机科学系"); SCComponent dptTwo=new Department(); dptTwo.setName("电子与信息工程系"); sCCOne.addSCc(dptOne); sCCOne.addSCc(dptTwo); SCComponent dptThree=new Department(); dptThree.setName("土木建筑系"); SCComponent dptFour=new Department(); dptFour.setName("工程管理系"); sCCTwo.addSCc(dptThree); sCCTwo.addSCc(dptFour); //sCCTwo.removeSCc(dptThree); head.printName(""); } }
运行结果如下:
XX大学
信息工程学院
计算机科学系
电子与信息工程系
建筑工程学院
工程管理系
安全模式:
代码如下:
package composite.sample.safety; /** * 院系抽像接口 * @ClassName: SCCompent * @author beteman6988 * @date 2017年12月9日 下午8:15:46 * */ public interface SCComponent { /** * get院系名称 * @Title: getName * @param @return * @return String * @throws */ public String getName(); /** * 设置院系名称 * @Title: setName * @param @return * @return * @throws */ public void setName(String name); /** * 打印院系名称 * @Title: printName * @param @param preStr * @return void * @throws */ public void printName(String preStr); }
package composite.sample.safety; /** * 叶子系节点 * @ClassName: Department * @author beteman6988 * @date 2017年12月9日 下午8:25:53 * */ public class Department implements SCComponent { private String departmentName; @Override public String getName() { // TODO Auto-generated method stub return departmentName; } @Override public void setName(String name) { this.departmentName=name; } @Override public void printName(String preStr) { System.out.println(preStr+this.departmentName); } }
package composite.sample.safety; import java.util.ArrayList; import java.util.List; /** *院系设置的组合对象 * @ClassName: SCComposite * @author beteman6988 * @date 2017年12月9日 下午8:37:44 * */ public class SCComposite implements SCComponent { private String name; private List<SCComponent> sCCList=new ArrayList<SCComponent>(); @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name=name; } public void addSCc(SCComponent scComponent) { if(sCCList.indexOf(scComponent)<0) { sCCList.add(scComponent); } } public void removeSCc(SCComponent scComponent) { this.sCCList.remove(scComponent); } @Override public void printName(String preStr) { System.out.println(preStr+this.name); for(SCComponent component:sCCList) { component.printName(preStr+" "); } } }
package composite.sample.safety; public class Client { public static void main(String[] args) { SCComposite head=new SCComposite(); head.setName("XX大学"); SCComposite sCCOne=new SCComposite(); sCCOne.setName("信息工程学院"); SCComposite sCCTwo=new SCComposite(); sCCTwo.setName("建筑工程学院"); head.addSCc(sCCOne); head.addSCc(sCCTwo); SCComponent dptOne=new Department(); dptOne.setName("计算机科学系"); SCComponent dptTwo=new Department(); dptTwo.setName("电子与信息工程系"); sCCOne.addSCc(dptOne); sCCOne.addSCc(dptTwo); SCComponent dptThree=new Department(); dptThree.setName("土木建筑系"); SCComponent dptFour=new Department(); dptFour.setName("工程管理系"); sCCTwo.addSCc(dptThree); sCCTwo.addSCc(dptFour); //sCCTwo.removeSCc(dptThree); head.printName(""); } }
运行结果如下:
XX大学
信息工程学院
计算机科学系
电子与信息工程系
建筑工程学院
土木建筑系
工程管理系
五、模式优缺点
优点:1.定义了包含基本对象和组合对象的类层次结构
2.统一了组合对象和叶子对象,在组合模式中,可以把叶子对象当成特殊的组合对象来看待,为他们定义统一的父类,从而把叶子对象和组合对象的行为统一起来。
3.方便扩展,只要是Component的子类对象,都可以很方便添加到组合对象中。
缺点:优点中的第3点既是优点,也是缺点,组合对象很难确定里面的具体组件类型,因为只要是Component的子类对象,都可以添加到组合对象中,所以当要确定组合对象中组件具体的类型时,就必须在运行期动态检测。