设计模式——模板设计模式(Template Method)

news2024/11/29 22:31:40

模板设计-base

什么是模板?

举个简单的例子,以AABB的格式,写出一个词语,你可能会想到,明明白白,干干净净等,
这个AABB就是一个模板,对模板心中有了一个清晰的概念之后,我们再来看今天我们的内容,模板设计。

实现思路

我们在父类中,定义处理流程的框架,子类中实现具体处理

要如何实现这样的思路呢?

在父类中定义多个抽象方法,然后由一个模板方法来进行调用,决定如何使用这些模板方法,就形成了处理流程的框架。

子类继承父类,只需要实现抽象方法。这样一来模板方法就留在了父类中,各个子类都可以有自己的实现方式。

代码实现

在这里插入图片描述

在这里插入图片描述

我们定义AbstractDisplay作为父类里面有open、print、close 3个抽象方法,一个display模板方法

charDisplay和StringDisplay作为AbstractDisplay的子类,去实现各自的open、print、close方法。
最终我们用子类统一去调用父类已经实现的display模板方法,查看效果。

public abstract class AbstractDisplay {
    /**
     * 交给字类实现的抽象方法(1) open
     */
    public abstract void open();
    /**
     * 交给字类实现的抽象方法(2)print
     */
    public abstract void print();
    /**
     * 交给字类实现的抽象方法(3)close
     */
    public abstract void close();

    /**
     * 模板方法,先调用open在调用5次print方法,最后调用close。
     * 可以看出,父类中只调用抽象方法,但不实现抽象方法,具体实现交给子类。
     */
    public final void display(){
        open();
        for (int i = 0; i < 5; i++) {
            print();
        }
        close();
    }
}

接下来让我们看看CharDisplayStringDisplay是如何实现的。

public class CharDisplay extends AbstractDisplay{
    private char character;

    public CharDisplay(char c){
        this.character = c;
    }

    @Override
    public void open() {
        System.out.print("<<");
    }

    @Override
    public void print() {
        System.out.print(character);
    }

    @Override
    public void close() {
        System.out.println(">>");
    }
}
public class StringDisplay extends AbstractDisplay{

    private String string;
    private Integer times;

    public StringDisplay(String string) {
        this.string = string;
        times = string.toCharArray().length;
    }



    @Override
    public void open() {
        System.out.print("+");
        for (int i = 0; i < times; i++) {
            System.out.print("-");
        }
        System.out.println("+");
    }

    @Override
    public void print() {
        System.out.println("|"+string+"|");
    }

    @Override
    public void close() {
        System.out.print("+");
        for (int i = 0; i < times; i++) {
            System.out.print("-");
        }
        System.out.println("+");
    }
}

让我们在Main类中调用试试看吧

public class Main3 {
    public static void main(String[] args) {
        //生成一个持有'H'的CharDisplay实例
        AbstractDisplay d1 = new CharDisplay('H');
        //生成一个持有'Hello,world.'的StringDisplay类的实例
        AbstractDisplay d2 = new StringDisplay("Hello,world.");
        //由于d1、d2都是AbstractDisplay的实例,可以调用继承的display方法,实际的程序行为取决于CharDisplay类和StringDisplay类的具体实现
        d1.display();
        d2.display();
    }
}

输出结果
在这里插入图片描述
我们创建了一个AbstractDisplay的模板,最终却生成了两种不同的结果!就像AABB的模板最终可以生成明明白白和干干净净一样!

恭喜你!!掌握了模板设计模式的基本使用!接下来让我们拓展一下思路。

思路拓展

可以使逻辑处理通用化

使用 Template Method模式究竟能带来什么好处呢?

让我们先看看Template Method模式的类图是什么样的
在这里插入图片描述

使用 Template Method模式究竟能带来什么好处呢?

这里,它的优点是由于在父类的模板方法中编写了算法,因此无需在每个子类中再重复编写算法。

例如,我们没使用 Template Method模式,而是使用文本编辑器的复制和粘贴功能编写了多个ConcreteClass角色。此时,会出现ConcreteClass1、ConcreteClass2、Concreteclass3 等很多相似的类。编写完成后立即发现了Bug还好,但如果是过一段时间才发现在Concreteclass1中有 Bug,该怎么办呢?这时,我们就必须将这个 Bug的修改反映到所有的 ConcreteClass 角色中才行。

关于这一点,如果是使用 Template Method模式进行编程,当我们在模板方法中发现Bug时只需要修改模板方法即可解决问题

父类与子类之间的协助

在 Template Method模式中,父类和子类是紧密联系、共同工作的。因此,在子类中实现父类中声明的抽象方法时,必须要理解这些抽象方法被调用的时机。在看不到父类的源代码的情况下想要编写出子类是非常困难的。

模板设计-plus

练习题一

java.io.Inputstream类使用了Template Method模式。请阅读官方文档(JDK的API参考资料 ),从中找出需要用java.io.Inputstream 的子类去实现的方法。

练习题二

上面给出的AbstractDisplay类的display方法如下

public final void display(){
	....
}
这里使用了final修饰符,请问这是想表达什么意思呢?

练习题三

如果想要让示例程序中的open、print、close方法可以被具有继承关系的类和同一程序包中的类调用,但是不能被无关的其他类调用,应当怎么做呢?

练习题四

Java中的接口与抽象类很相似。接口同样也是抽象方法的集合,但是在TemplateMethod 模式中,我们却无法使用接口来扮演AbstractClass角色,请问这是为什么呢?

答案

练习题一

查阅文档后,你就会知道,InputStream是一个抽象类,其中需要子类实现的抽象方法只有一个就是read方法啦!
在这里插入图片描述

在这里插入图片描述

练习题二

final关键字修饰方法的时候,表示定义在父类中的模板方法display无法在子类中进行重写,这正和我们期望的一致,我们希望模板方法由父类直接实现,即父类直接定义抽象方法的使用框架,不希望子类再去进行重写,使用模板设计模式时,子类也不应该重写模板方法。

练习题三

可以将AbstractDisplay类中的open,print,close方法的可见性声明为protected。这样就可以让继承该类的子类调用这些方法,而其他包中的类无法调用这些方法(不过同一个包中的类依然可以调用这些方法)。

练习题四

这是因为 TemplateMethod模式中的AbstractClass角色必须实现处理的流程。在抽象类中可以实现一部分方法(例如 AbstractDisplay类中的display方法),但是在接口中是无法实现方法的。因此,在TemplateMethod 模式中,无法用接口替代抽象类。

在Java8之前以上结论成立,在Java8之后,引入了defualt关键字,增强了接口的功能,使得接口也可以实现方法。

知识关联性

Factory Method 模式(工厂模式)

是将 Template Method 模式用于生成实例的一个典型例子。

Strategy模式(策略模式)

在 Template Method 模式中,可以使用继承改变程序的行为。这是因为 Template Method 模式在父类中定义程序行为的框架,在子类中决定具体的处理。
与此相对的是 Strategy模式,它可以使用委托改变程序的行为。与Template Method 模式中改变部分程序行为不同的是,Strategy模式用于替换整个算法。

模板设计模式(Template Method)的讲解到此就结束啦,感谢阅读。💕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1667969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[hpssupfast@mailfence.com].Elbie勒索病毒如何恢复数据和预防

[hpssupfastmailfence.com].Elbie是一种新型勒索病毒,快速恢复重要数据请添加技术服务号(safe130)。以下是关于elbie勒索病毒的详细信息&#xff1a; 病毒介绍&#xff1a; elbie勒索病毒&#xff0c;也称为PHOBOS勒索软件&#xff0c;它通过加密文件并要求支付赎金以恢复对文…

spring模块(六)spring监听器(1)ApplicationListener

一、介绍 1、简介 当某个事件触发的时候&#xff0c;就会执行的方法块。 当然&#xff0c;springboot很贴心地提供了一个 EventListener 注解来实现监听。 2、源码&#xff1a; package org.springframework.context;import java.util.EventListener; import java.util.fu…

【数据结构】双向循环链表专题解析

实现自己既定的目标&#xff0c;必须能耐得住寂寞单干。&#x1f493;&#x1f493;&#x1f493; 目录 •✨说在前面 &#x1f34b;知识点一&#xff1a;双向链表的结构 • &#x1f330;1."哨兵位"节点 • &#x1f330;2.双向带头循环链表的结构 &#x1f34b;…

Java - Json字符串转List<LinkedHashMap<String,String>>

需求&#xff1a;在处理数据时&#xff0c;需要将一个Object类型对象集合转为有序的Map类型集合。 一、问题 1.原代码&#xff1a; 但在使用时出现报错&#xff1a; Incompatible equality constraint: LinkedHashMap<String, String> and LinkedHashMap 不兼容的相等…

社区送水小程序软件开发

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 前端开发:vue.js 可选语言&#xff1a;pythonjavanode.jsphp均支持 运行软件…

猫头虎分享已解决Bug || 已解决ERROR: Ruby Gems安装中断 ⚠️ Bug 报告:Gem::RemoteFetcher::FetchError

猫头虎分享已解决Bug || 已解决ERROR: Ruby Gems安装中断 ⚠️ Bug 报告&#xff1a;Gem::RemoteFetcher::FetchError 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; …

四川汇昌联信:拼多多运营属于什么行业?

拼多多运营属于什么行业?这个问题看似简单&#xff0c;实则涉及到了电商行业的深层次理解。拼多多运营&#xff0c;顾名思义&#xff0c;就是在拼多多这个电商平台上进行商品销售、推广、客户服务等一系列活动。那么&#xff0c;这个行业具体包含哪些内容呢?下面就从四个不同…

redis深入理解之实战

1、SpringBoot整合redis 1.1 导入相关依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId&g…

Webstorm免费安装教程

一、介绍 WebStorm 具有智能化的代码编辑功能&#xff0c;如代码补全、重构、代码导航、错误检测等等&#xff0c;这些功能可以帮助开发人员提高编码效率&#xff0c;减少出错的可能性。同时&#xff0c;WebStorm 也集成了各种流行的前端框架和库&#xff0c;如 React、Angula…

Python3实现三菱PLC串口通讯(附源码和运行图)

基于PyQt5通过串口通信控制三菱PLC 废话不多说&#xff0c;直接上源码 """ # -*- coding:utf-8 -*- Project : Mitsubishi File : Main_Run.pyw Author : Administrator Time : 2024/05/09 下午 04:10 Description : PyQt5界面主逻辑 Software:PyCharm "…

【Linux】轻量级应用服务器如何开放端口 -- 详解

一、测试端口是否开放 1、测试程序 TCP demo 程序&#xff08;可参考&#xff1a;【Linux 网络】网络编程套接字 -- 详解-CSDN博客&#xff09; 2、测试工具 Windows - cmd 窗口 输入命令&#xff1a;telnet [云服务器的公网ip] [port] 二、腾讯云安全组开放端口 1、安全组设…

简单贪吃蛇的实现

贪吃蛇的实现是再windows控制台上实现的&#xff0c;需要win32 API的知识 Win32 API-CSDN博客https://blog.csdn.net/bkmoo/article/details/138698452?spm1001.2014.3001.5501 游戏说明 ●地图的构建 ●蛇身的移动&#xff08;使用↑ . ↓ . ← . → 分别控制蛇的移动&am…

【C++】list原理讲解及其实现

目录 一、认识list底层结构 二、list的构造类函数 三、迭代器 四、数据的访问 五、容量相关的函数 六、关于数据的增删查改操作 前言 要模拟实现list&#xff0c;必须要熟悉list的底层结构以及其接口的含义&#xff0c;在上一篇我们仔细讲解了list的常见接口的使用及其含义&…

consul启动Error_server_rejoin_age_max (168h0m0s) - consider wiping your data dir

consul 启动报错&#xff1a; consul[11880]: 2024-05-12T08:37:51.095-0400 [ERROR] agent: startup error: error"refusing to rejoin cluster because server has been offline for more than the configured server_rejoin_age_max (168h0m0s) - consider wiping you…

IntelliJ的Maven编译返回找不到有效证书

文章目录 小结问题及解决找不到有效证书找不到org.springframework.stereotype.Service问题IntelliJ: Cannot resolve symbol springframework 参考 小结 将IntelliJ工程拷贝到新的机器中&#xff0c;返回Maven编译返回找不到有效证书的问题&#xff0c;进行了解决。 问题及解…

通过内网穿透实现远程访问个人电脑资源详细过程(免费)(NatApp + Tomcat)

目录 1. 什么是内网穿透 2. 内网穿透软件 3. NatApp配置 4. 启动NatApp 5. 通过内网穿透免费部署我们的springboot项目 通过内网穿透可以实现远程通过网络访问电脑的资源&#xff0c;本文主要讲述通过内网穿透实现远程访问个人电脑静态资源的访问&#xff0c;下一章节将讲…

Java入门基础学习笔记18——赋值运算符

赋值运算符&#xff1a; 就是“”&#xff0c;就是给变量赋值的&#xff0c;从右边往左边看。 int a 10; // 把数据赋值给左边的变量a存储。 扩展赋值运算符&#xff1a; 注意&#xff1a;扩展的赋值运算符隐含了强制类型转换。 package cn.ensource.operator;public class…

Unity Animation--动画窗口指南(使用动画视图)

Unity Animation--动画窗口指南&#xff08;使用动画视图&#xff09; 使用动画视图 window -> Animation 即可打开窗口 查看GameObject上的动画 window -> Animation -> Animation 默认快捷键 Ctrl 6 动画属性列表 在下面的图像中&#xff0c;“动画”视图&am…

【LAMMPS学习】八、基础知识(6.3)使用 LAMMPS GUI

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语,以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各种模拟。 …

合并连个有序链表(递归)

21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 2.讲解算法原理 2.1重复子问题 2.2只关心其中的一个子问题是如何解决的 2.3细节&#xff0c;递归出口 3.小总结 &#xff08;循环&#xff08;迭代&#xff09;VS 递归&#xff09;&#xff08;递归VS深搜&…