【设计模式系列 14】组合模式及其在 JDK 和 MyBatis 源码中的运用
===============================================================
组合模式有两种写法,分别是透明模式和安全模式。下面我们就以高考的科目为例来看看组合模式是如何体现在代码中的
1、首先建立一个顶层的抽象科目类,这个类中定义了三个通用操作方法,但是均默认不支持操作
package com.zwx.design.pattern.composite.transparency;
/**
顶层抽象组件
*/
public abstract class GkAbstractCourse {
public void addChild(GkAbstractCourse course){
System.out.println("不支持添加操作");
}
public String getName() throws Exception {
throw new Exception("不支持获取名称");
}
public void info() throws Exception{
throw new Exception("不支持查询信息操作");
}
}
PS:这个类是抽象类,但是在这里并没有将这些方法定义为抽象方法,而是默认都抛出异常。这么做的原因是假如定义为抽象方法,那么所有的子类都必须重写父类方法。但是这种通过抛异常的方式,如果子类需要用到的功能就重写覆盖父类方法即可,不需要用到的方法直接无需重写。
2、新建一个普通科目类继承通用科目抽象类,这个类作为叶子节点,没有重写 addChild 方法,也就是这个类属于叶子节点,不支持添加子节点:
package com.zwx.design.pattern.composite.transparency;
/**
普通科目类(叶子节点)
*/
public class CommonCource extends GkAbstractCourse {
private String name;//课程名称
private String score;//课程分数
public CommonCource(String name, String score) {
this.name = name;
this.score = score;
}
@Override
public String getName(){
return this.name;
}
@Override
public void info() {
System.out.println("课程:" + this.name + ",分数:" + score);
}
}
3、建立一个具有层级的节点,三个方法都重写了,支持添加子节点,这个类里面为了方便打印的时候看出层级关系,所以我定义了一个层级属性。
package com.zwx.design.pattern.composite.transparency;
import java.util.ArrayList;
import java.util.List;
/**
树枝节点
*/
public class LevelCource extends GkAbstractCourse{
private List<GkAbstractCourse> courseList = new ArrayList<>();
private String name;
private int level;
public LevelCource(String name, int level) {
this.name = name;
this.level = level;
}
@Override
public void addChild(GkAbstractCourse course) {
courseList.add(course);
}
@Override
public String getName(){
return this.name;
}
@Override
public void info() throws Exception {
System.out.println("课程:" + this.name);
for (GkAbstractCourse course : courseList){
for (int i=0;i<level;i++){
System.out.print(" ");
}
System.out.print(">");
course.info();
}
}
}
4、建立一个测试类来测试一下:
package com.zwx.design.pattern.composite.transparency;
public class TestTransparency {
public static void main(String[] args) throws Exception {
GkAbstractCourse ywCourse = new CommonCource("语文","150");
GkAbstractCourse sxCourse = new CommonCource("数学","150");
GkAbstractCourse yyCourse = new CommonCource("英语","150");
GkAbstractCourse wlCourse = new CommonCource("物理","110");
GkAbstractCourse hxCourse = new CommonCource("化学","100");
GkAbstractCourse swCourse = new CommonCource("生物","90");
GkAbstractCourse lzCourse = new LevelCource("理综",2);
lzCourse.addChild(wlCourse);
lzCourse.addChild(hxCourse);
lzCourse.addChild(swCourse);
GkAbstractCourse gkCourse = new LevelCource("理科高考科目",1);
gkCourse.addChild(ywCourse);
gkCourse.addChild(sxCourse);
gkCourse.addChild(yyCourse);
gkCourse.addChild(lzCourse);
gkCourse.info();
}
}
输出结果:
课程:理科高考科目
课程:语文,分数:150
课程:数学,分数:150
课程:英语,分数:150
课程:理综
课程:物理,分数:110
课程:化学,分数:100
课程:生物,分数:90
这里如果用普通科目去调用 add 方法就会抛出异常,假如上面调用:
swCourse.addChild(ywCourse);
会输出
不支持添加操作
因为在普通科目类里面并没有重写 addChild 方法。
透明模式的特点就是将组合对象所有的公共方法都定义在了抽象组件内,这样做的好处是客户端无需分辨当前对象是属于树枝节点还是叶子节点,因为它们具备了完全一致的接口,不过缺点就是叶子节点得到到了一些不属于它的方法,比如上面的 addChild 方法,这违背了接口隔离性原则。
安全组合模式只是规定了系统各个层次的最基础的一致性行为,而把组合(树节点)本身的方法(如树枝节点管理子类的 addChild 等方法)放到自身当中。
1、首先还是建立一个顶层的抽象根节点(这里面只定义了一个通用的抽象 info 方法):
packag
e com.zwx.design.pattern.composite.safe;
package com.zwx.design.pattern.composite.safe;
/**
顶层抽象组件
*/
public abstract class GkAbstractCourse {
protected String name;
protected String score;
public GkAbstractCourse(String name, String score) {
this.name = name;
this.score = score;
}
public abstract void info();
}
2、建立一个叶子节点(这里只是重写了 info 方法,没有定义其他特有方法):
package com.zwx.design.pattern.composite.safe;
/**
叶子节点
*/
public class CommonCource extends GkAbstractCourse {
public CommonCource(String name,String score) {
super(name,score);
}
@Override
public void info() {
System.out.println("课程:" + this.name + ",分数:" + this.score);
}
}
3、定义一个树枝节点(这个类当中定义了一个树枝特有的方法 addChild):
package com.zwx.design.pattern.composite.safe;
import java.util.ArrayList;
import java.util.List;
/**
树枝节点
*/
public class LevelCource extends GkAbstractCourse{
private List<GkAbstractCourse> courseList = new ArrayList<>();
private int level;
public LevelCource(String name, String score,int level) {
super(name,score);
this.level = level;
}
public void addChild(GkAbstractCourse course) {
courseList.add(course);
}
@Override
public void info() {
System.out.println("课程:" + this.name + ",分数:" + this.score);
for (GkAbstractCourse course : courseList){
for (int i=0;i<level;i++){
System.out.print(" ");
}
System.out.print(">");
course.info();
}
}
}
4、新建测试类来测试:
package com.zwx.design.pattern.composite.safe;
public class TestSafe {
public static void main(String[] args) throws Exception {
CommonCource ywCourse = new CommonCource("语文","150");
CommonCource sxCourse = new CommonCource("数学","150");
CommonCource yyCourse = new CommonCource("英语","150");
CommonCource wlCourse = new CommonCource("物理","110");
CommonCource hxCourse = new CommonCource("化学","100");
CommonCource swCourse = new CommonCource("生物","90");
LevelCource lzCourse = new LevelCource("理综","300",2);
lzCourse.addChild(wlCourse);
lzCourse.addChild(hxCourse);
lzCourse.addChild(swCourse);
LevelCource gkCourse = new LevelCource("理科高考","750",1);
gkCourse.addChild(ywCourse);
gkCourse.addChild(sxCourse);
gkCourse.addChild(yyCourse);
gkCourse.addChild(lzCourse);
gkCourse.info();
}
}
评论