结构型模式之组合模式


我们知道古代的皇帝想要管理国家,是不可能直接管理到具体的每一个百姓的,因此设置了很多的机构,比如说三省六部,这些机构下边又会有很多的小组织,他们共同的管理这个国家。
再比如现在的公司, 下面有很多的部门,每个部门下有会分组。
在软件开发中也是这样,例如,文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等。对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便。

组合模式的定义

组合(Composite)模式的定义
有时又叫作部分-整体模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点),用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性,属于结构型模式

组合模式的结构

组合模式包含以下主要角色:
抽象构件(Component):
它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。

树枝节点(Composite):
是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

树叶节点(Leaf):
是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中声明的公共接口。

组合模式的实现

组合模式分为透明式的组合模式和安全式的组合模式。

透明式:

在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。

/**
 * 抽象构件:课程
 */
public abstract class CourseComponent {
    //添加子节点的方法
    public void addChild(CourseComponent catalogComponent){
        throw new UnsupportedOperationException("不支持添加操作");
    }
    //删除子节点的方法
    public void removeChild(CourseComponent catalogComponent){
        throw new UnsupportedOperationException("不支持删除操作");
    }
    //获取课程名字
    public String getName(CourseComponent catalogComponent){
        throw new UnsupportedOperationException("不支持获取名称操作");
    }
    //获取课程价格
    public double getPrice(CourseComponent catalogComponent){
        throw new UnsupportedOperationException("不支持获取价格操作");
    }
    //打印信息
    public void print(){
        throw new UnsupportedOperationException("不支持打印操作");
    }
}
/**
 * 树枝节点:
 */
public class CoursePackage extends CourseComponent {
    private List<CourseComponent> items = new ArrayList<CourseComponent>();//用于存储子节点
    private String name;//课程名称
    private Integer level;//目录级别
    public CoursePackage(String name, Integer level) {
        this.name = name;
        this.level = level;
    }
    @Override
    public void addChild(CourseComponent catalogComponent) {
        items.add(catalogComponent);
    }
    @Override
    public String getName(CourseComponent catalogComponent) {
        return this.name;
    }
    @Override
    public void removeChild(CourseComponent catalogComponent) {
        items.remove(catalogComponent);
    }
    @Override
    public void print() {
        System.out.println(this.name);
        for(CourseComponent catalogComponent : items){
            //控制显示格式
            if(this.level != null){
                for(int  i = 0; i < this.level; i ++){
                    //打印空格控制格式
                    System.out.print("  ");
                }
                for(int  i = 0; i < this.level; i ++){
                    //每一行开始打印一个+号
                    if(i == 0){ System.out.print(">"); }
                    System.out.print("-");
                }
            }
            //打印标题
            catalogComponent.print();
        }
    }
}
/**
 * 树叶节点
 */
public class Course extends CourseComponent {
    private String name;
    private double price;
    public Course(String name, double price) {
        this.name = name;
        this.price = price;
    }
    @Override
    public String getName(CourseComponent catalogComponent) {
        return this.name;
    }
    @Override
    public double getPrice(CourseComponent catalogComponent) {
        return this.price;
    }
    @Override
    public void print() {
        System.out.println(name + " (¥" + price + "元)");
    }
}
/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        System.out.println("============透明组合模式===========");

        CourseComponent javaBase = new Course("Java入门课程",16800);//创建叶子节点
        CourseComponent ai = new Course("人工智能",6800);//添创建叶子节点
        CourseComponent packageCourse = new CoursePackage("Java架构师课程",2);//创建2级树枝节点

        CourseComponent design = new Course("Java设计模式",3000);
        CourseComponent source = new Course("源码分析",3000);
        CourseComponent softSkill = new Course("软技能",3000);
        //将上面的三个叶子节点插入到树枝节点
        packageCourse.addChild(design);
        packageCourse.addChild(source);
        packageCourse.addChild(softSkill);

        CourseComponent catalog = new CoursePackage("课程主目录",1);//创建1级树枝节点
        catalog.addChild(javaBase);//添加叶子节点
        catalog.addChild(ai);//添加叶子节点
        catalog.addChild(packageCourse);//添加树枝节点
        catalog.print();//打印该节点下的信息
    }
}

程序运行结果如下:呈层级显示

============透明组合模式===========
课程主目录
  >-Java入门课程 (16800.0)
  >-人工智能 (6800.0)
  >-Java架构师课程
    >--Java设计模式 (3000.0)
    >--源码分析 (3000.0)
    >--软技能 (3000.0)

安全式:

在该方式中,将管理叶子节点的方法移到树枝节点中,抽象构件和叶子节点没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道叶子节点和树枝节点的存在,所以失去了透明性。

/**
 * 抽象构件:目录
 */
public abstract class Directory {
    protected String name;
    public Directory(String name) {
        this.name = name;
    }
    public abstract void show();
}
/**
 * 树枝节点:文件夹
 */
public class Folder extends Directory {
    private List<Directory> dirs;//存放叶子节点
    private Integer level;
    public Folder(String name,Integer level) {
        super(name);
        this.level = level;
        this.dirs = new ArrayList<Directory>();
    }
    @Override
    public void show() {
        System.out.println(this.name);
        for (Directory dir : this.dirs) {
            //控制显示格式
            if(this.level != null){
                for(int  i = 0; i < this.level; i ++){
                    //打印空格控制格式
                    System.out.print("  ");
                }
                for(int  i = 0; i < this.level; i ++){
                    //每一行开始打印一个+号
                    if(i == 0){ System.out.print(">"); }
                    System.out.print("-");
                }
            }
            //打印名称
            dir.show();
        }
    }
    public boolean add(Directory dir) {
        return this.dirs.add(dir);
    }
    public boolean remove(Directory dir) {
        return this.dirs.remove(dir);
    }
    public Directory get(int index) {
        return this.dirs.get(index);
    }
    public void list(){
        for (Directory dir : this.dirs) {
            System.out.println(dir.name);
        }
    }
}
/**
 * 叶子节点:文件
 */
public class File extends Directory {
    public File(String name) {
        super(name);
    }
    @Override
    public void show() {
        System.out.println(this.name);
    }
}
/**
 * 测试类
 */
class Test {
    public static void main(String[] args) {

        System.out.println("============安全组合模式===========");
        File qq = new File("QQ.exe");//叶子节点
        File wx = new File("微信.exe");//叶子节点
        
        Folder office = new Folder("办公软件",2);//2级树枝节点
        
        File word = new File("Word.exe");//叶子节点
        File ppt = new File("PowerPoint.exe");//叶子节点
        File excel = new File("Excel.exe");//叶子节点
        //将上边三个叶子节点插入2级树枝节点
        office.add(word);
        office.add(ppt);
        office.add(excel);

        Folder wps = new Folder("金山软件",3);//3级树枝节点
        wps.add(new File("WPS.exe"));//将叶子节点插入3级树枝节点
        office.add(wps); //将三级树枝节点插入2级树枝节点

        Folder root = new Folder("根目录",1);//1级树枝节点
        root.add(qq);
        root.add(wx);
        root.add(office);

        System.out.println("----------show()方法展示所有层级目录-----------");
        root.show();//展示所有层级目录
        System.out.println("----------list()方法展示1级树枝节点-----------");
        root.list();//展示1级树枝节点
    }
}

程序运行结果如下:

============安全组合模式===========
----------show()方法展示所有层级目录-----------
根目录
  >-QQ.exe
  >-微信.exe
  >-办公软件
    >--Word.exe
    >--PowerPoint.exe
    >--Excel.exe
    >--金山软件
      >---WPS.exe
----------list()方法展示1级树枝节点-----------
QQ.exe
微信.exe
办公软件

组合模式的应用场景

1.在需要表示一个对象整体与部分的层次结构的场合。
2.要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
3.当子系统与其内各个对象层次呈现树形结构时(如,树形菜单,操作系统目录结构,公司组织架构等)

组合模式的优缺点

组合模式的主要优点有:
1.清楚的定义分层次的复杂对象,表示对象的全部或部分层次
2.让客户端忽略层次的差异,方便对整个层次结构进行控制
3.简化客户端代码
4.符合开闭原则

其主要缺点是:
1.设计较复杂,客户端需要花更多时间理清类之间的层次关系;
2.不容易限制容器中的构件;
3.不容易用继承的方法来增加构件的新功能;
4.使设计变得更抽象。

<<上一篇:享元模式
>>下一篇:模板方法模式

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值