秦怀杂货店

General Store

  • 首页
  • 文章归档
  • 标签
  • 分类
  • 关于页面

  • 搜索
随便聊聊 数据结构 小游戏 数据库 Docker Springboot 系统设计 雪花算法 分布式 海量ip 最长回文子串 算法 面试题 线程池 多线程 线程 java学习 布隆过滤器 github 架构设计 docsify Git JVM LeetCode 杂货思考 设计模式 Lambda native isAssignableFrom 反射 剑指Offer mybatis SPI JDBC 编程工具 Java基础 集合

设计模式【13】-- 模板模式怎么弄?

发表于 2022-02-10 | 分类于 设计模式 | 0 | 阅读次数 253

设计模式

开局还是那种图,各位客官往下看...

张无忌学太极拳,忘记了所有招式,打倒了"玄冥二老",所谓"心中无招"。设计模式可谓招数,如果先学通了各种模式,又忘掉了所有模式而随心所欲,可谓OO之最高境界。

模板模式是什么?

模板模式,同样是一种行为型模式,也就是关于对象做什么或者怎么做的设计模式。模板模式的本质需要定义操作中的算法的框架,但是有一些步骤,又不需要具体的实现,而是不同的子类各自实现。子类不能修改流程框架,但是部分的步骤可以做定制化的实现。

主要要解决一个问题:一些通用的方法,但是每一个子类却都重新写,冗余。

比如说,做菜的步骤一般是:洗锅 --> 炒菜 --> 洗碗 ,不同的菜,只是炒菜这一个步骤具体细节是不同的,但是其他步骤确实几乎一模一样的,这样其实整体框架,以及重复的步骤,我们可以抽象到模板中,而不同的细节方法可以开放给每一种菜(具体实现)去定制。

又比如造房子的时候,很多地方的建造都是一样的:地基,墙壁,水管等等,但是不同的房子里面的内部的设计又有所不同。

不使用模板模式

就挑个简单的例子“炒菜”,如果不使用模板模式的话,糖醋鲤鱼:


public class SweetAndSourCarp {

    public void cookFood(){
        washPan();
        cook();
        eat();
        washDishes();
        System.out.println("");
    }

    private void washPan(){
        System.out.print("洗锅 --> ");
    }

    private void cook(){
        System.out.print("煮糖醋鲤鱼 --> ");
    }

    private void eat(){
        System.out.print("吃饭 --> ");
    }

    private void washDishes(){
        System.out.print("洗碗 --> ");
    }
}

再弄一个农家小炒肉,需要写很多相同的方法:

public class ShreddedPorkWithVegetables {

    public void cookFood(){
        washPan();
        cook();
        eat();
        washDishes();
        System.out.println("");
    }

    private void washPan(){
        System.out.print("洗锅 --> ");
    }

    private void cook(){
        System.out.print("炒农家小炒肉 --> ");
    }

    private void eat(){
        System.out.print("吃饭 --> ");
    }

    private void washDishes(){
        System.out.print("洗碗 --> ");
    }
}

测试类如下:

public class Test {
    public static void main(String[] args) {
        SweetAndSourCarp sweetAndSourCarp = new SweetAndSourCarp();
        sweetAndSourCarp.cookFood();

        ShreddedPorkWithVegetables shreddedPorkWithVegetables = new ShreddedPorkWithVegetables();
        shreddedPorkWithVegetables.cookFood();
    }
}

测试结果:

洗锅 --> 煮糖醋鲤鱼 --> 吃饭 --> 洗碗 --> 
洗锅 --> 炒农家小炒肉 --> 吃饭 --> 洗碗 --> 

可以看到,整体流程是一样的,有些步骤一样,有些步骤不一样,但是不使用模板模式,需要每个类都重写一遍方法,即使是通用方法,整个流程都需要自己写一遍。

使用模板模式优化

如果使用模板模式,那么我们会抽象出一个抽象类,定义整体的流程,已经固定的步骤,开放需要定制的方法,让具体的实现类按照自己的需求来定制。

定义的抽象类:

public abstract class CookFood {
    public final void cookFood() {
        washPan();
        cook();
        eat();
        washDishes();
        System.out.println("");
    }

    private final void washPan() {
        System.out.print("洗锅 --> ");
    }

    public abstract void cook();

    private final void eat() {
        System.out.print("吃饭 --> ");
    }

    private final void washDishes() {
        System.out.print("洗碗 --> ");
    }
}

具体的实现类糖醋鲤鱼:

public class SweetAndSourCarp extends CookFood {
    @Override
    public void cook() {
        System.out.print("煮糖醋鲤鱼 --> ");
    }
}

农家小炒肉:

public class ShreddedPorkWithVegetables extends CookFood {
    @Override
    public void cook() {
        System.out.print("炒农家小炒肉 --> ");
    }
}

测试类与前面的一样,测试结果也一样,这里不再重复。

上面的方法中,其实我们只开放了cook()方法,这就是钩子方法:

在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为 ”钩子方法”

钩子方法是开放的,可以由子类随意覆盖,但是像上面的其他方法,我们不希望子类重写或者覆盖它,就可以用 final 关键字,防止子类重写模板方法。

模板模式的应用

其实在 JDK 的 Thread 实现中,就是使用了模板模式,我们知道创建线程有两个方式:

  • 创建 Thread 类
  • 实现 runnable 接口

我们实现的一般是 run() 方法, 但是调用的却是 start() 方法来启动线程,这个原因就是 start() 方法里面帮我们调用了run()方法, run()方法是开发的方法,我们可以覆盖重写它。

Start0()是一个native方法,是由 c 语言去实现的,在调用的时候,真正调用了我们的 run() 方法,如果需要跟踪这个方法需要到 HotSpot底层去。这里介绍的目的是让大家了解,它同样是使用了模板模式。

    private native void start0();

了解 native 关键字可以参考:http://aphysia.cn/archives/native

模板模式的优缺点

模板模式的优点:

  • 1、封装固定的部分,拓展需要定制修改的部分,符合开闭原则。
  • 2、公共的代码在父类中,容易维护。
  • 3、整个流程由父类把握,调整比较方便。

缺点:

  • 1、子类可能会很多,系统复杂度上升。
  • 2、子类只有一小部分实现,了解全部方法则需要在父类中阅读,影响代码阅读。

总结:代码该隐藏的复杂细节隐藏起来,开放定制化部分,优雅!

设计模式系列:

  • 设计模式【1】-- 单例模式到底几种写法?
  • 设计模式【1.1】-- 你想如何破坏单例模式?
  • 设计模式【1.2】-- 枚举式单例有那么好用么?
  • 设计模式【1.3】-- 为什么饿汉式单例是线程安全的?
  • 设计模式【2】-- 简单工厂模式了解一下?
  • 设计模式【2.1】-- 简单工厂模式怎么演变成工厂方法模式?
  • 设计模式【2.2】-- 工厂模式怎么演变成抽象工厂模式?
  • 设计模式【3.1】-- 浅谈代理模式之静态、动态、cglib代理
  • 设计模式【3.2】-- JDK动态代理源码分析有多香?
  • 设计模式【3.3】-- CGLIB动态代理源码解读
  • 设计模式【4】-- 建造者模式详解
  • 设计模式【5】-- 原型模式
  • 设计模式【6.1】-- 初探适配器模式
  • 设计模式【6.2】-- 再聊聊适配器模式
  • 设计模式【7】-- 探索一下桥接模式
  • 设计模式【8】-- 手工耿教我写装饰器模式
  • 设计模式【9】-- 外观模式?没那么高大上
  • 设计模式【10】-- 顺便看看享元模式
  • 设计模式【11】-- 搞定组合模式
  • 设计模式【12】-- 搞定最近大火的策略模式

【作者简介】:
秦怀,公众号【秦怀杂货店】作者,个人网站:http://aphysia.cn,技术之路不在一时,山高水长,纵使缓慢,驰而不息。

剑指Offer全部题解PDF

开源编程笔记

  • 本文作者: 秦怀杂货店
  • 本文链接: http://aphysia.cn/archives/she-ji-mo-shi-13--mo-ban-mo-shi-zen-me-nong-
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 随便聊聊 # 数据结构 # 小游戏 # 数据库 # Docker # Springboot # 系统设计 # 雪花算法 # 分布式 # 海量ip # 最长回文子串 # 算法 # 面试题 # 线程池 # 多线程 # 线程 # java学习 # 布隆过滤器 # github # 架构设计 # docsify # Git # JVM # LeetCode # 杂货思考 # 设计模式 # Lambda # native # isAssignableFrom # 反射 # 剑指Offer # mybatis # SPI # JDBC # 编程工具 # Java基础 # 集合
设计模式【12】-- 搞定最近大火的策略模式
设计模式【14】-- 从智能音箱中学习命令模式
  • 文章目录
  • 站点概览
秦怀杂货店

秦怀杂货店

纵然缓慢,驰而不息。

145 日志
19 分类
37 标签
Github E-mail
Creative Commons
0%
© 2022 秦怀杂货店
由 Halo 强力驱动
|
主题 - NexT.Pisces v5.1.4