Combined mode coding

首先我们来创建一个组合模式的包,新建一个包名,composite,那我们现在引入一个业务场景,我们就拿一个网站来说,

这里面有很多课程,也有课程目录,那课程有名称与价格,例如说JAVA的课程,那JAVA的课程分属于JAVA课程这个目录下,

JAVA课程这个目录,有很多JAVA课程,Python课程目录有很多Python课程,如果我们使课程的目录和课程呢,继承同一个

抽象类,例如目录组件,那就可以把课程本身,还有由课程组合成的目录呢,视为同一类对象,进行操作,但是在操作上,

还会有一些差别,具体的差别定制化处理,例如说打印这个操作,我们可以打印一个课程的名称和价格,我们也可以打印

这个课程目录,如果我们打印这个课程目录,就会把这个课程目录下边所有的课程打印出来,对于这个操作我们可以认为,

是一类操作,对他们两是想通的,那我们首先创建一个抽象的上层组件,目录组件,CatalogComponent

看看组合模式的树状结构,我们把课程和课程目录全部都继承目录组件,对于print方法他们两可以一致的处理,

对于其他不一样的方法呢,在这个抽象父类上,会有默认的实现,例如说抛出异常,所以当Course去调用add和remove的时候,

就会抛出异常,同时课程目录去getPrice的时候,也会抛出异常,因为这个目录本身他没有价格,当然我们也可以重写,比如

这个目录一旦获取价格的话,就代表获取这个目录下边所有课程价格的和,这样也是可以的,还是看我们具体的业务场景,

那在我们这里边为了区分度,getPrice我们就认为这个目录本身是没有价值的,例如你们在学习网上也不可能花钱,去买一个

课程目录来,那对于name课程目录是可以重写的,那我们现在回到这个类里面,getName已经加入进来了

而这两个的关系可以看出来,是一个组合关系,这个菱形的箭头,可以看出来这里有一个星号,那这个星就是代表多的意思,

例如说items,这个是一个集合,那UML非常清晰,现在来写一下,测试类,直接创建一个Test
package com.learn.design.pattern.structural.composite;

/**
 * CatalogComponent这个类是抽象类
 * 对于这个抽象类里边是有默认实现的
 * 交给子类来决定
 * 是否需要重写他
 * 因为课程目录和课程本身
 * 这两个层次的对象
 * 操作并不完全一样
 * 但是呢又有一样的
 * 所以我们直接在父类上把这些方法的实现直接都写完
 * 然后交由子类选择性的重写
 * 那这个抽象组件就写好了
 * 接下来我们会使课程和课程目录
 * 这两个类来继承这个抽象类
 * 现在我们来创建一个课程类
 * 
 * 
 * @author Leon.Sun
 *
 */
public abstract class CatalogComponent {
	/**
	 * 那我们写一个add
	 * add操作呢肯定是add自己本身
	 * 然后我们交由课程和课程目录来继承这个抽象类
	 * 同理我们再增加一些其他的
	 * 
	 * 
	 * @param catalogComponent
	 */
    public void add(CatalogComponent catalogComponent){
    	/**
    	 * 这里直接写上不支持添加操作
    	 * 
    	 * 
    	 */
        throw new UnsupportedOperationException("不支持添加操作");
    }

    /**
     * 从第二个开始remove
     * 
     * 
     * @param catalogComponent
     */
    public void remove(CatalogComponent catalogComponent){
    	/**
    	 * 不支持删除操作
    	 * 
    	 * 
    	 */
        throw new UnsupportedOperationException("不支持删除操作");
    }


    /**
     * 第三个就是获取名称
     * 返回值就是String类型
     * 
     * 
     * @param catalogComponent
     * @return
     */
    public String getName(CatalogComponent catalogComponent){
    	/**
    	 * 不支持获取名称操作
    	 * 
    	 * 
    	 */
        throw new UnsupportedOperationException("不支持获取名称操作");
    }


    /**
     * 这个方法需要改造成getPrice
     * 获取具体的价格
     * 返回值是double
     * 
     * 
     * @param catalogComponent
     * @return
     */
    public double getPrice(CatalogComponent catalogComponent){
    	/**
    	 * 不支持获取价格操作
    	 * 
    	 * 
    	 */
        throw new UnsupportedOperationException("不支持获取价格操作");
    }


    /**
     * 下边就是打印
     * 打印很好理解
     * 如果组件是课程
     * 那就打印课程
     * 如果组件是课程目录
     * 那就打印这个课程目录下面的课程
     * 
     * 然后来到最上层的抽象类
     * 也把里面的入参去掉
     * 这样就可以了
     * 
     * 
     */
    public void print(){
    	/**
    	 * 不支持打印操作
    	 * 
    	 * 
    	 */
        throw new UnsupportedOperationException("不支持打印操作");
    }
}
package com.learn.design.pattern.structural.composite;

/**
 * 我们使他继承CatalogComponent
 * 这个课程的类就写好了
 * 然后我们再写一个课程的目录类
 * 我们再创建一个类
 * CourseCatalog
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Course extends CatalogComponent {
	/**
	 * 他有两个属性
	 * 课程的名字
	 * 
	 */
    private String name;
    /**
     * 还有课程的价格
     * 这里面就用小double了
     * 我们主要关注组合模式
     * 
     * 
     */
    private double price;

    /**
     * 我们把构造器写一下
     * 
     * 
     * @param name
     * @param price
     */
    public Course(String name, double price) {
        this.name = name;
        this.price = price;
    }

    /**
     * 还有我们要重写抽象类的方法
     * 重写哪几个方法呢
     * 首先对于add
     * add是目录类的方法
     * add只有目录可以add
     * 课程不能在他的子节点再添加课程了
     * 但是课程目录可以
     * remove也是同理
     * 课程目录可以保存
     * 可以remove一个课程
     * 也可以add一个课程
     * 但是课程本身可以获取课程的名字
     * 课程的报价
     * 还有打印具体的课程
     * 所以重写这三个方法
     * 
     * 
     */
    @Override
    public String getName(CatalogComponent catalogComponent) {
    	/**
    	 * 这里的实现我们也要改一下
    	 * return什么呢
    	 * this.name
    	 * 
    	 * 
    	 */
        return this.name;
    }

    @Override
    public double getPrice(CatalogComponent catalogComponent) {
    	/**
    	 * 价格也是一样
    	 * this.price
    	 * 
    	 * 
    	 */
        return this.price;
    }

    /**
     * 课程里的入参也不需要
     * 因为打印的时候
     * 打印的name和price呢
     * 也是课程本身的
     * 
     * 
     */
    @Override
    public void print() {
    	/**
    	 * print这里面我们也重写一下
    	 * 
    	 * 
    	 */
        System.out.println("Course Name:"+name+" Price:"+price);
    }
}
package com.learn.design.pattern.structural.composite;

import java.util.ArrayList;
import java.util.List;

/**
 * 他同样的继承CatalogComponent
 *
 * 
 * @author Leon.Sun
 *
 */
public class CourseCatalog extends CatalogComponent {
	/**
	 * 在目录里面肯定有很多的课程
	 * 而这个课程又是目录组件
	 * 所以我们可以在这里面声明一个
	 * 目录组件是个抽象类
	 * 
	 * 
	 */
    private List<CatalogComponent> items = new ArrayList<CatalogComponent>();
    /**
     * 目录本身还有name
     * 比如说这个是JAVA课程的目录
     * 那这个就是他的名称
     * 
     * 
     */
    private String name;
    /**
     * 增加这么一个属性
     * 
     * 
     */
    private Integer level;


    /**
     * 然后我们写一下他的构造器
     * 
     * 然后把level传进来
     * 
     * 
     * 
     * @param name
     * @param level
     */
    public CourseCatalog(String name,Integer level) {
        this.name = name;
        /**
         * 然后在这里面赋值
         * 为什么要用Integer呢
         * 因为如果用int的话
         * 那么他会有默认值的
         * 所以我们使用Integer
         * 然后进行空判断
         * 往下走
         * 
         * 
         */
        this.level = level;
    }

    /**
     * 现在我们可以继续重写它的方法
     * 对于目录来说add和remove
     * 都是有的
     * 同时它还可以打印
     * 所以我们重写这三个方法
     * 那add的实现也很简单
     * 直接用 items.add
     * 把catalogComponent这个放进来
     * 就可以了
     * 
     * 
     */
    @Override
    public void add(CatalogComponent catalogComponent) {
        items.add(catalogComponent);
    }

    /**
     * 我们把getName写一下
     * 这里面也很简答
     * return this.name;
     * 就可以了
     * 来到UML
     * getName已经加入进来了
     * 
     */
    @Override
    public String getName(CatalogComponent catalogComponent) {
        return this.name;
    }

    /**
     * remove也是同理
     * 直接items.remove
     * 把catalogComponent这个对象放进来
     * 
     * 
     */
    @Override
    public void remove(CatalogComponent catalogComponent) {
        items.remove(catalogComponent);
    }

    /**
     * 在打印的时候呢
     * 我们要遍历这个目录
     * 而这个目录就是他本身
     * 所以这个print方法在封装的时候
     * 他并不需要入参
     * 所以这里我们可以干掉
     * 这个是通过一个业务场景来分析
     * 这个方法的入参的
     * 那我们来到Course里边
     * 
     * 所以根据不同的业务场景
     * 我们如果封装组合模式
     * 具体的入参
     * 还有返回值
     * 到底是什么类型
     * 有没有这个参数等等
     * 都要根据实际的业务来确定
     * 那print也非常的简单
     * 我们直接一个for循环
     * 就可以了
     * 
     * 来到这里边
     * 
     * 
     * 
     */
    @Override
    public void print() {
    	/**
    	 * 直接输出一个this.name
    	 * 课程目录
    	 * 
    	 * 
    	 */
        System.out.println(this.name);
        /**
         * 循环items
         * 
         * 
         */
        for(CatalogComponent catalogComponent : items){
        	/**
        	 * 然后我们在输出课程的时候
        	 * 我们再增加一个空格
        	 * 
        	 * 如果this.level不等于空的话
        	 * 那这里面就调用一下for循环
        	 * 来拼接这个空格
        	 * 
        	 * 为什么用this呢
        	 * 我们也可以通过instanceof这个关键字
        	 * 来判断这个类型
        	 * 是目录类型还是课程类型
        	 * 如果是目录类型
        	 * 再打印级别的空格个数
        	 * 那样也是可以的
        	 * 为什么这里使用this呢
        	 * 因为使用this的时候呢
        	 * 肯定是课程目录本身来执行
        	 * 那我们现在回到Test里边
        	 * 
        	 * 
        	 */
            if(this.level != null){
                for(int  i = 0; i < this.level; i++){
                	/**
                	 * 也就是我们在打印目录的时候
                	 * 里面多了一些空格
                	 * 这样在排版的时候
                	 * 就很容易看清楚
                	 * 他们的从属关系
                	 * 我们回到Test里边
                	 * 
                	 * 在这里面再打印空格就OK了
                	 * 
                	 * 
                	 */
                    System.out.print("  ");
                }
            }
            /**
             * 直接调用catalogComponent.print
             * 这样这个方法的实现就写好了
             * 接下来我们来看一下他的UML图
             * 这个位置刚刚好
             * 
             * 
             */
            catalogComponent.print();
        }
    }
}
package com.learn.design.pattern.structural.composite;

/**
 * 
 * @author Leon.Sun
 *
 */
public class Test {
	/**
	 * 直接写一个主函数
	 * 现在就把课程目录和课程
	 * 都认为是CatalogComponent
	 * 就可以了
	 * 现在我们把课程和课程目录
	 * 都认为是CatalogComponent
	 * 他们两都是这个类型
	 * 进行统一处理
	 * 
	 * 
	 * @param args
	 */
    public static void main(String[] args) {
    	/**
    	 * 首先我们创建一个CatalogComponent
    	 * linuxCourse
    	 * new一个课程
    	 * 名字"Linux课程"
    	 * 价格呢是11
    	 * 
    	 * 
    	 */
        CatalogComponent linuxCourse = new Course("Linux课程",11);
        /**
         * 再new一个
         * windows课程
         * 操作系统课程
         * 那对于操作系统的课程
         * 我们学习网的目录
         * 创建好了
         * 
         * 
         * 
         */
        CatalogComponent windowsCourse = new Course("Windows课程",11);

        /**
         * 然后我们再创建一个JAVA课程的目录
         * 这回我们要创建目录了
         * javaCourseCatalog
         * new一个Course目录
         * 这里面放课程名称
         * "Java课程目录"
         * 现在就把我们的课程
         * 填进来
         * 
         * JAVA课程是二级目录
         * 这个时候我们来run一下
         * 看一下结果
         * 结果已经出来了
         * 我们来看一下
         * 现在就非常清晰了
         * 主目录是学习网课程主目录
         * 然后下一层有三个课程
         * 分别是linux windows 
         * 还有一个课程是课程目录
         * 然后在JAVA课程目录里面又有三个
         * JAVA课程
         * 这样通过这个排版
         * 他们之间的父子关系
         * 就非常清晰了
         * 那组合模式coding就完成了
         * 组合模式使用的时候坑非常多
         * 我们把坑都埋到了coding实战当中
         * 如果我们非常顺利的编写下来的话
         * 你们对于组合模式
         * 应用可能不会太深
         * 通过这么一个踩坑的方式
         * 目的是希望你们在使用组合模式的时候
         * 刚刚这些点一定要注意
         * 同时希望你们
         * 对这一块的理解能够更加深入一些
         * 
         * 
         */
        CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程目录",2);

        /**
         * 我们有mmall电商一期
         * mmallCourse1
         * new一个Course
         * 名字是"Java电商一期"
         * 价格呢是55
         * 
         * 
         */
        CatalogComponent mmallCourse1 = new Course("Java电商一期",55);
        /**
         * 然后再创建一个二期
         * mmallCourse2
         * 价格是66
         * 
         * 
         */
        CatalogComponent mmallCourse2 = new Course("Java电商二期",66);
        /**
         * 再创建一个设计模式
         * designPattern
         * "Java设计模式"
         * 价格是77
         * 
         * 
         */
        CatalogComponent designPattern = new Course("Java设计模式",77);

        /**
         * 目录里面添加这三个JAVA课程
         * 这三个课程就添加到JAVA课程里面
         * 那么我们还可以创建一个目录
         * 
         * 
         */
        javaCourseCatalog.add(mmallCourse1);
        javaCourseCatalog.add(mmallCourse2);
        javaCourseCatalog.add(designPattern);

        /**
         * 这个目录是一个主目录
         * 我们就叫imoocMainCourseCatalog
         * 学习网的课程主目录
         * 我们写一下他的名称
         * "学习网课程主目录"
         * 
         * 我们在构造这个目录的时候
         * 需要传一个级别了
         * 例如这个主目录是一个一级目录
         * 
         * 
         * 
         */
        CatalogComponent imoocMainCourseCatalog = new CourseCatalog("学习网课程主目录",1);
        /**
         * 然后我们向学习网的课程主目录里面添加
         * 添加什么呢
         * 我们首先添加一个linux课程
         * 
         */
        imoocMainCourseCatalog.add(linuxCourse);
        /**
         * 再添加一个windows课程
         * 
         * 
         */
        imoocMainCourseCatalog.add(windowsCourse);
        /**
         * 再添加一个JAVA课程的目录
         * 例如说add这个方法
         * 我可以填课程
         * 也可以填目录
         * 把他两视为一个对象
         * 减少这两个对象在使用的时候差异
         * 这个就是组合模式的核心
         * 然后我们调用一下
         * 
         * 
         * 
         */
        imoocMainCourseCatalog.add(javaCourseCatalog);

        /**
         * 调用一下他的打印方法
         * 打印学习网的课程主目录
         * 结果已经出来了
         * 这个课程下边有linux课程
         * 有windows课程
         * 还有他的价格
         * 三个JAVA课程
         * 那现在在输出中已经把它们打平了
         * 因为我们在目录里边
         * 并没有写上具体的目录名称
         * 所以我们现在来加上
         * 我们再run一下
         * 结果已经出来了
         * 我们看一下
         * 主目录加了两个空格
         * 在主目录下面有两个操作系统的课程
         * 然后是JAVA课程目录
         * Java课程目录和他两是平级的
         * 然后在JAVA课程目录下边
         * 又有三个JAVA课程
         * 那我们看一下
         * 他的缺点是什么呢
         * 也就是如果我们要动态的
         * 对类型进行限制的话
         * 会使这个业务逻辑变得复杂
         * 我们现在想在JAVA目录本身是一个二级目录
         * 因为学习网主目录是一级目录
         * 他呢是二级目录
         * 对这个空格打印的时候
         * 需要打印两个空格
         * 从而在打印上来区分
         * 从属关系
         * 那在这里就需要进行动态的判断了
         * 所以这个坑也是埋了一下
         * 为了让你们能体会到
         * 组合模式的缺点
         * 所以我们这代码还要改
         * 我们现在想一下
         * 刚才我们都改什么了
         * 首先对于组合模式
         * 最上层的抽象组件
         * 参数和返回值
         * 这里要多花心思
         * 来确定他
         * 还有目录在重写方法的时候
         * 来确定哪些组合业务逻辑的重写
         * 哪些是不符合业务逻辑的
         * 例如目录本身并没有价格
         * 那如果我们重写为这个目录获取价格的时候
         * 代表获取这个目录下边所有课程的价格
         * 那我们还要考虑
         * 一旦这个目录下边还有目录的话
         * 是不是要做深层次的递归
         * 那这些都是我们要考虑的点
         * 就例如我们现在碰到的问题
         * 我们需要排版更清晰的
         * 从属关系
         * 那我们还要在目录这个结构下
         * 增加一个level
         * 这么一个属性
         * 那很简单
         * 来增加一个属性
         * 
         * 
         * 
         * 
         */
        imoocMainCourseCatalog.print();


    }
}

 

Guess you like

Origin blog.csdn.net/Leon_Jinhai_Sun/article/details/90902471