在编码原则中,有一条是:多用组合,少用继承。这里面的组合与我们今天要学的组合模式是不等价的,这里的组合其实是一种聚合,而聚合和组合有着本质的区别。
组合模式
1、组合和聚合
聚合各个对象聚合在一起工作,但是”我“没有你也行,”我“照样可以正常运行工作。聚合对象不具备相同生命周期。
组合各个对象之间组成了一个整体,缺一不可,”我“没有了你,可能我就不能正常运行工作,可能功能上就会造成很大的缺陷。组合对象具有相同的生命周期。
示例
上述例子可能有些许不妥当,但也能较为直观的说清楚二者的区别。
2、什么是组合模式
组合模式(Composite Pattern):也称之为整体-部分(Part-Whole)模式,是用于把一组相似的对象当作一个单一的对象。组合模式的核心是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得单个对象和组合对象的使用具有一致性,组合模式属于结构型模式。
组合模式又分为:透明模式和安全模式
3、透明模式
示例:我们通过高考科目来举例说明组合模式中的透明模式
package cn.liangyy.composite.transparency;
/**
* 高考科目的抽象类
*/
public abstract class GkAbstractCourse {
/**
* 添加子节点
* @param course
*/
public void addChild(GkAbstractCourse course){
System.out.println("不支持添加操作!");
}
/**
* 获取当前节点名称
* @return
* @throws Exception
*/
public String getName() throws Exception {
throw new Exception("不支持获取名称!");
}
/**
* 获取高考科目信息
* @throws Exception
*/
public void info() throws Exception {
throw new Exception("不支持查询信息操作!");
}
}
复制代码
想必这里大家有疑问,为什么不将这些方法定义为抽象方法,而是默认都抛出异常?假如定义为抽象方法,那么所有的子类都必须重写父类方法。这种抛异常的方式,完全根据子类需要用到的功能去重写覆盖父类方法即可。
package cn.liangyy.composite.transparency;
/**
* 普通高考科目类(叶子节点)
* 无法包含其他科目
*/
public class LeafCource extends GkAbstractCourse {
//课程名称
private String name;
//课程分数
private String score;
public LeafCource(String name, String score) {
this.name = name;
this.score = score;
}
/**
* 获取当前节点名称
* @return
* @throws Exception
*/
@Override
public String getName() {
return this.name;
}
/**
* 获取高考科目信息
* @throws Exception
*/
@Override
public void info() {
System.out.println("课程:"+ this.name + ",分数:"+ score);
}
}
复制代码
package cn.liangyy.composite.transparency;
import java.util.ArrayList;
import java.util.List;
/**
* 树枝类
* 可以包含其他科目的类
*/
public class BranchCource extends GkAbstractCourse {
private List<GkAbstractCourse> courseList = new ArrayList<>();
//科目名称
private String name;
//层级
private int level;
public BranchCource(String name, int level) {
this.name = name;
this.level = level;
}
/**
* 添加子节点(子课程)
* @param course
*/
@Override
public void addChild(GkAbstractCourse course) {
courseList.add(course);
}
/**
* 获取当前节点名称(课程名称)
* @return
* @throws Exception
*/
@Override
public String getName() throws Exception {
return this.name;
}
/**
* 获取高考科目信息
* @throws Exception
*/
@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();
}
}
}
复制代码
package cn.liangyy.composite.transparency;
/**
* 透明模式-测试
*/
public class TestTransparency {
public static void main(String[] args) throws Exception {
GkAbstractCourse ywCourse = new LeafCource("语文","150");
GkAbstractCourse sxCourse = new LeafCource("数学","150");
GkAbstractCourse yyCourse = new LeafCource("英语","150");
GkAbstractCourse wlCourse = new LeafCource("物理","110");
GkAbstractCourse hxCourse = new LeafCource("化学","100");
GkAbstractCourse swCourse = new LeafCource("生物","90");
GkAbstractCourse lzCourse = new BranchCource("理综",2);
lzCourse.addChild(wlCourse);
lzCourse.addChild(hxCourse);
lzCourse.addChild(swCourse);
GkAbstractCourse lkgkCourse = new BranchCource("理科高考",1);
lkgkCourse.addChild(ywCourse);
lkgkCourse.addChild(sxCourse);
lkgkCourse.addChild(yyCourse);
lkgkCourse.addChild(lzCourse);
lkgkCourse.info();
//这里会输出"不支持添加操作!",叶子节点无法再添加
ywCourse.addChild(sxCourse);
}
}
复制代码
上面就是一个透明模式的写法,它的父类将所有的方法都定义好了,对各个子类完全透明,所有才将它称为透明模式。优点:客户端无需分辨当前对象时属于树枝节点还是叶子节点,因为它们具备了完全一致的接口。缺点:就是叶子节点能得到不属于它的方法,比如上述例子中的==ywCourse.addChild(sxCourse);==,这个很明显违背了接口隔离性原则。
4、安全模式
我们通过高考科目来举例说明组合模式中的安全模式
package cn.liangyy.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();
}
复制代码
package cn.liangyy.composite.safe;
/**
* 普通高考科目类(叶子节点)
*/
public class LeafCource extends GkAbstractCourse {
public LeafCource(String name,String score){
super(name,score);
}
/**
* 获取课程信息
*/
@Override
public void info() {
System.out.println("课程:"+ this.name +",分数:"+ this.score);
}
}
复制代码
package cn.liangyy.composite.safe;
import java.util.ArrayList;
import java.util.List;
/**
* 树枝类
*/
public class BranchCource extends GkAbstractCourse {
//子课程
private List<GkAbstractCourse> courseList = new ArrayList<>();
//层级
private int level;
public BranchCource(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();
}
}
}
复制代码
package cn.liangyy.composite.safe;
/**
* 安全模式-测试
*/
public class TestSafe {
public static void main(String[] args) {
GkAbstractCourse ywCourse = new LeafCource("语文","150");
GkAbstractCourse sxCourse = new LeafCource("数学","150");
GkAbstractCourse yyCourse = new LeafCource("英语","150");
GkAbstractCourse wlCourse = new LeafCource("物理","110");
GkAbstractCourse hxCourse = new LeafCource("化学","100");
GkAbstractCourse swCourse = new LeafCource("生物","90");
BranchCource lzCourse = new BranchCource("理综","300",2);
lzCourse.addChild(wlCourse);
lzCourse.addChild(hxCourse);
lzCourse.addChild(swCourse);
BranchCource lkgkCourse = new BranchCource("理科高考","750",1);
lkgkCourse.addChild(ywCourse);
lkgkCourse.addChild(sxCourse);
lkgkCourse.addChild(yyCourse);
lkgkCourse.addChild(lzCourse);
lkgkCourse.info();
}
}
复制代码
组合模式适用场景组合模式一般应用在有层级关系的场景,最经典的就是树形菜单、文件和文件夹的管理等。
组合模式优点组合模式清楚的定义了分层次的复杂对象,让客户端可以忽略层次的差异,方便对整个层次进行动态控制。
组合模式缺点组合模式的叶子和树枝的声明是实现类而不是接口,违反了依赖倒置原则,而且组合模式会使设计更加抽象不好理解。
评论