Template Method pattern coding

行为模式的包,然后创建一个templatemethod模板方法这样的一个包,这里面我们引入一个业务场景,例如说

制作课程,这么一个业务场景,我现在在制作这个视频课程,那对于后端的JAVA课程,还有设计模式这个课程,

在制作的时候,要制作PPT,制作视频,还要决定去写一个手记,还要提供前端的一些素材,例如前端的很多课程呢,

都是要提供前端等素材的,而我们这个设计模式,又不提供图片素材,那不同的课程,制作的步骤主线上是一样的,

但是在细分的一些细节上,还是有所不同,我们这个设计模式,一些图片的素材,我们是不需要提供的,而且本身我们

也没有,那么是否去写手记,这个呢还不一定,有的课程制作的时候必须有配套的手记,那有一些并不是必须的,写手记

并不是非必选项,完全留给子类来扩展,那么我们把这个方法做成钩子方法,那具体怎么实现呢,现在就让我们一起来coding,

很简单,这个设计模式非常非常简单,首先我们创建一个类,这个类是什么呢,抽象类ACourse

这个设计模式学起来也是比较轻松的,就是一个单纯的抽象类,然后下边实现了抽象方法,

Test我们先不看,我们主要看上边,右侧有一个needWriteArticle,这个方法正是重写父类的needWriteArticle,

其他的没有变化,那我们再看一下前端课程这个类,我们要再演进一版,什么业务场景呢,FECourse,这个呢是一个前端

课程,它是一个泛指,例如说,React,Vue,这些都是FECourse,对于后端课程来说,这个是一个具体的设计模式课程,当然我们

没有起后端课程这么一个类,所以我们现在聚焦在FECourse,Vue需要写手记,而React不需要写手记,但是我们的这个类声明的

是FECourse,那如果我们这么写,我们看一下

不要关注Test,还是看上边,模板方法的UML,清晰明了,非常容易理解,所以呢总结来说,对于我们这个模板

方法设计模式,这个ACourse,也就是这个抽象类,就是一个模板,定义了一套标准,他呢是一种行为模式,把这些makePPT,

makeVideo,这种固定的写法,这些步骤,以及这些顺序呢,都固定好,对于需要钩子方法的这些选择性方法,开放出去,

而子类对于writeArticle实现是没有疑义的,而对于packageCourse,完全交给子类来实现,这里面要注意一下这个final,

一定要加final,否则子类是可以重写makeCourse,也就是把步骤全部打乱,都OK的,模板方法就是为了定义一个模板,

不想其他类把流程步骤给打乱,那模板方法这个模式的coding呢,就到这里,相信你们,设计模式肯定能学会,而且肯定能

理解好
package com.learn.design.pattern.behavioral.templatemethod;

/**
 * 它是一个抽象类
 * 
 * 
 * @author Leon.Sun
 *
 */
public abstract class ACourse {

	/**
	 * 首先这里要写一个protected的方法
	 * 这个权限控制
	 * 这个方法声明成final的
	 * 因为在ACourse里面定义的模板
	 * 是不想被修改的
	 * 所以这个方法声明成final的
	 * 我们都用void makeCourse
	 * 制作课程
	 * 这里面注意
	 * 声明final是不希望子类可以覆盖这个方法
	 * 防止修改我们这个制作课程流程的执行顺序
	 * 那这个方法先放到这儿
	 * 现在我们来拆解一下这个小步骤
	 * 首先我们要做一个PPT
	 * 没什么说的
	 * 所有的课程都要制作PPT
	 * 无论现在网络上是免费的课程
	 * 还是收费的课程
	 * 
	 * 这里面要怎么写呢
	 * 首先我们这个类是为了定义一个模板
	 * 那这个模板关键方法是makeCourse
	 * 首先我们调用一下makePPT
	 * 必须要制作PPT
	 * 然后要makeVideo
	 * 就是必须要makeVideo
	 * 那对于是否写手记来配套这个课程
	 * 是可选的
	 * 我们交由钩子方法来实现
	 * 比如这里有一个needWriteArticle方法
	 * 如果这个方法返回true的话
	 * 我们调用writeArticle
	 * 那这里就使用了模板方法里面的钩子方法
	 * 最后我们再调用一下packageCourse
	 * 那这里面刚刚也说了
	 * 对于makePPT makeVideo writeArticle这里面的实现
	 * 对于所有子类是没有疑义的
	 * 所以我们声明为final的
	 * 
	 * 
	 * 
	 */
    protected final void makeCourse(){
        this.makePPT();
        this.makeVideo();
        if(needWriteArticle()){
            this.writeArticle();
        }
        this.packageCourse();
    }

    /**
     * 直接制作PPT
     * 你们想一下void makePPT
     * 这个方法是不是也要是final的呢
     * 如果这个方法所有的子类都不需要重写的话
     * 那我们就要把它声明成final的
     * 也就是说这个行为是固定不变的
     * 制作什么课程
     * 都要制作PPT
     * 并且这里面的实现
     * 是满足所有子类的
     * 
     * 
     */
    final void makePPT(){
        System.out.println("制作PPT");
    }
    /**
     * 同理制作视频
     * 那我们这里的制作都用make这个动词了
     * 那我们再想象一下
     * 
     * 
     */
    final void makeVideo(){
        System.out.println("制作视频");
    }
    /**
     * 编写手记
     * 编写手记他是固定的
     * 就是编写手记
     * 玩不出来什么花样
     * 所以对于编写手记这个实现
     * 所有的子类是认可的
     * 但是小明要编写手记
     * 小刚就不编写手记了
     * 也就是说对于这个课程来说
     * 编写手记是一个可选项
     * 但是对于这里的实现
     * 子类都是认可的
     * 所以写手记这个方法
     * 我们也把他声明成final的
     * 所以我们声明一个钩子方法
     * 
     * 
     */
    final void writeArticle(){
        System.out.println("编写手记");
    }
    //钩子方法
    /**
     * 返回值是布尔
     * 是否需要编写手记
     * 默认return false
     * 并且他不是final的
     * 子类可以覆盖他
     * 写完钩子方法之后我们要写一个钩子方法
     * 
     * 
     * @return
     */
    protected boolean needWriteArticle(){
        return false;
    }
    /**
     * 包装课程
     * 那对于设计模式这个包装来说呢
     * 我们要把项目的源码
     * 放到这个素材里面
     * 而对于一些前端的课程呢
     * 也要放源码
     * 他们还要放一些图片的素材
     * 也就是对于不同的课程
     * 包装课程
     * 上线之前
     * 提供的素材呢
     * 都是不一样的
     * 而把这个方法
     * 完全交给子类来实现
     * 所以对于使用模板方法这个设计模式的时候
     * 我们对于业务的一个模型
     * 一定要抽象化
     * 对于扩展性以及业务后续的发展
     * 哪些行为是固定的
     * 哪些行为是要交给子类的
     * 哪些行为是可选的
     * 这些都要把这个业务分析透
     * 然后再来设计这个模板方法
     * 设计模式的使用场景
     * 那现在我们来声明具体的子类实现课程
     * 例如DesignPatternCourse
     * 
     * 
     */
    abstract void packageCourse();
}
package com.learn.design.pattern.behavioral.templatemethod;

/**
 * 我们让他继承ACourse
 * 实现里面的具体方法
 * 现在抽象方法只有一个
 * 
 * 
 * @author Leon.Sun
 *
 */
public class DesignPatternCourse extends ACourse {
	/**
	 * 打包我们写一下具体的实现
	 * 提供课程的JAVA源代码
	 * 因为现在设计的业务场景
	 * 已经覆盖了模板方法的业务场景
	 * 所以对于设计模式这个课程来说
	 * 在包装课程的时候
	 * 我们需要提供JAVA源代码
	 * 我们再写一个SECourse
	 * 前端课程
	 * 
	 * 可以来到这里试一下
	 * 我们发现makePPT makeVideo makeArticle并不能重写
	 * 这正是final的一个作用
	 * 那我们现在回到测试函数里边
	 * 
	 * 
	 */
    @Override
    void packageCourse() {
        System.out.println("提供课程Java源代码");
    }

    @Override
    protected boolean needWriteArticle() {
        return true;
    }

}
package com.learn.design.pattern.behavioral.templatemethod;

/**
 * 让他继承ACourse
 * 这里面也很简单
 * 
 * 
 * @author Leon.Sun
 *
 */
public class FECourse extends ACourse {
	/**
	 * 默认是false
	 * 
	 * 
	 */
    private boolean needWriteArticleFlag = false;
    
    /**
     * 首先提供课程的前端代码
     * 然后呢还要提供一个
     * 提供课程的图片等多媒体素材
     * 视频等等之类的
     * 视频嵌入到前端页面里
     * 使用的多媒体素材
     * 那我们现在来看一下
     * 他的UML图
     * 
     * 
     */
    @Override
    void packageCourse() {
        System.out.println("提供课程的前端代码");
        System.out.println("提供课程的图片等多媒体素材");
    }

    /**
     * 现在我们使用构造器的方式
     * 那么我们再来到Test里边
     * 
     * 
     * @param needWriteArticleFlag
     */
    public FECourse(boolean needWriteArticleFlag) {
        this.needWriteArticleFlag = needWriteArticleFlag;
    }

    /**
     * 代表所有的前端类都要写手记
     * 这个又不符合业务扩展的一个方向了
     * 那怎么办呢
     * 很简单
     * 把我们现在的模板方法模式呢
     * 再演进一步
     * 我们把needWriteArticle
     * 再开放给Test应用层
     * 怎么开放呢
     * 也是非常容易的
     * 
     * 然后我们再重写这个方法
     * 返回值是什么呢
     * 就是this.needWriteArticleFlag
     * 然后我们通过构造器和setter的方式呢
     * 把needWriteArticleFlag开放给应用层
     * 
     * 
     * 
     */
    @Override
    protected boolean needWriteArticle() {
        return this.needWriteArticleFlag;
    }
}
package com.learn.design.pattern.behavioral.templatemethod;

/**
 * 直接run一下
 * 看一下结果
 * 首先对于后端设计模式开始了
 * 制作PPT
 * 制作视频
 * 提供课程的JAVA源代码
 * end
 * 前端课程我们看一下
 * 制作PPT
 * 制作视频
 * 提供课程的前端代码
 * 提供课程的图片多媒体素材
 * 前端课程end
 * 那现在我们就要考虑一下
 * writeArticle写手记这么一个方法
 * 上线是有编写课程手记
 * 所以就想重写这个方法
 * needWriteArticle
 * return什么呢
 * return true
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Test {
    public static void main(String[] args) {
    	  /**
    	   * 首先我们输出一个后端设计模式课程start
    	   * 
    	   * 
    	   */
//        System.out.println("后端设计模式课程start---");
    	  /**
    	   * 直接new一个DesignPatternCourse
    	   * 通过父类声明一个引用来指向子类的一个实例
    	   * 
    	   * 因为Test里面new的是一个子类对象
    	   * 所以只要我们在这个类里面重写的话
    	   * 那对于模板里面要执行方法的时候
    	   * 拿到的就是子类的这个方法的返回值
    	   * 那对于现在这个设计模式的这个课程
    	   * 他会编写手记
    	   * 因为needWriteArticle默认的是返回false
    	   * 那我们再run一下
    	   * 可以看到后端课程设计的时候
    	   * 有一个编写手记
    	   * 但是前端并没有
    	   * 这个呢和预期一样
    	   * 因为前端并没有编写手记的方法
    	   * 而是选用了模板默认返回值false
    	   * 那这个就是钩子方法的具体使用
    	   * 非常简单
    	   * 我们看一下UML有什么变化呢
    	   * 
    	   * 
    	   */
//        ACourse designPatternCourse = new DesignPatternCourse();
    	  /**
    	   * 直接调用它的makeCourse()
    	   * 制作课程
    	   * 
    	   * 
    	   */
//        designPatternCourse.makeCourse();
    	  /**
    	   * 然后写一个end
    	   * 
    	   * 
    	   */
//        System.out.println("后端设计模式课程end---");


        System.out.println("前端课程start---");
        /**
         * 写一个前端的课程
         * 
         * 现在我们想让我们的前端课程写手记
         * 赋值一个true
         * 上边我们先注释上
         * run一下
         * 编写手记了
         * 我们再把它改成false
         * 再run一下
         * 制作课程下边并没有编写手记
         * 那这里面想说明什么呢
         * 我特意设计这个case
         * 就想说对于模板方法
         * 抽象的层次
         * 例如说
         * 设计模式这个课程
         * 那在我们这个业务场景中
         * 我们把它定义为后端的一个课程
         * 其中的一个子课程
         * 而FECourse是一个大范围的课程
         * 例如说React
         * JavaScript
         * 或者呢
         * Vue这些
         * 都是前端课程
         * 如果我们没业务类型转化不够合理的话
         * 就会发生刚刚我写的这个case
         * 他两可以理解在我们这个业务场景中
         * 并不是同一个level的
         * 也就是设计模式的上一级
         * 所以这种情况
         * 我们有可能把适当的权限开放给应用层
         * 所以刚刚通过这种方式
         * 声明一个自己的变量
         * 然后通过构造器或者setter的方式呢
         * 把这个flag开放给应用层
         * 那这种使用方式
         * 还是要看具体的应用场景
         * 现在的这种写法是为了满足不同的FECourse
         * 对于手记可能有不同需求的
         * 实现满足方案
         * 那我们再看一下UML
         * 
         * 
         * 
         */
        ACourse feCourse = new FECourse(false);
        feCourse.makeCourse();
        /**
         * 前端课程end
         * 
         * 
         */
        System.out.println("前端课程end---");


    }
    
}

 

Guess you like

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