《图解设计模式》笔记(二)交给子类

news2024/11/15 11:08:45

三、Template Method模式:将具体处理交给子类

示例程序类图

在这里插入图片描述

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

角色

在这里插入图片描述

  • AbstractClass(抽象类)

    不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。

    这些抽象方法由子类ConcreteClass角色负责实现。

    在示例程序中,由AbstractDisplay类扮演此角色。

  • ConcreteClass(具体类)

    该角色负责具体实现AbstractClass角色中定义的抽象方法。这里实现的方法将会在AbstractClass角色的模板方法中被调用。

    在示例程序中,由CharDisplay类和stringDisplay类扮演此角色。

扩展思路的要点

可以使逻辑处理通用化

本模式的好处:在父类的模板方法中编写了算法,无需在每个子类中再编写算法。若模板方法中发现Bug,只需修改模板方法即可。

假设没使用Template Method模式,而是复制粘贴编写了多个ConcreteClass角色,会出现ConcreteClassl、ConcreteClass2、ConcreteClass3等很多相似的类。其中一个有bug,其他类似的都得跟着再改一遍。

父类与子类之间的协作

本模式中,父类和子类是紧密联系、共同工作的。

因此,在子类中实现父类中声明的抽象方法时,必须要理解这些抽象方法被调用的时机。若看不到父类的源代码,很难编写出子类。

父类与子类的一致性

在示例程序中,不论是CharDisplay的实例还是stringDisplay的实例,都是先保存在AbstractDisplay类型的变量中,然后再来调用display方法的。

使用父类类型的变量保存子类实例的优点是:即使没有用instanceof等指定子类的种类,程序也能正常工作。

无论在父类类型的变量中保存哪个子类的实例,程序都可以正常工作,这种原则称为里氏替换原则(The Liskov Substitution Principle,LSP)。当然,LSP并非仅限于Template Method模式,它是通用的继承原则。

相关的设计模式

Factory Method模式(第4章)

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

Strategy模式(第10章)

在Template Method模式中,可以使用继承改变程序的行为。这是因为Template Method模式在父类中定义程序行为的框架,在子类中决定具体的处理。

与此相对的是Strategy模式,它可以使用委托改变程序的行为。

与Template Method模式中改变部分程序行为不同的是,Strategy模式用于替换整个算法。

四、Factory Method模式:将实例的生成交给子类

示例程序类图

在这里插入图片描述

Factory.java中的create方法如下,另外两个方法都是abstract

public final Product create(String owner) {
    Product p = createProduct(owner);
    registerProduct(p);
    return p;
}

测试

public static void main(String[] args) {
    Factory factory = new IDCardFactory();
    Product card1 = factory.create("小明");
    Product card2 = factory.create("小红");
    Product card3 = factory.create("小刚");
    card1.use();
    card2.use();
    card3.use();
}

角色

父类(框架)这一方的Creator角色和Product角色的关系,与子类(具体加工)这一方的ConcreteCreator角色和ConcreteProduct角色的关系,是平行的。

在这里插入图片描述

  • Product(产品)

    属于框架这一方,是一个抽象类。

    它定义了在Factory Method模式中生成的那些实例所持有的接口(API),但具体的处理则由子类ConcreteProduct角色决定。

    在示例程序中,由Product类扮演此角色。

  • Creator(创建者)

    属于框架这一方,是负责生成Product角色的抽象类,但具体的处理则由子类ConcreteCreator角色决定。

    在示例程序中,由Factory类扮演此角色。

    Creator角色对于实际负责生成实例的ConcreteCreator角色一无所知,它只知道:调用Product角色和生成实例的方法(图中的factoryMethod方法),就可以生成Product的实例。

    在示例程序中,createProduct方法是用于生成实例的方法。生成实例不是用new关键字而是调用生成实例的专用方法,这样可以防止父类与其他具体类耦合。

  • ConcreteProduct(具体的产品)

    属于具体加工这一方,它决定了具体的产品。

    在示例程序中,由IDCard类扮演此角色。

  • ConcreteCreator(具体的创建者)

    属于具体加工这一方,它负责生成具体的产品。

    在示例程序中,由IDCardFactory类扮演此角色。

扩展思路的要点

框架与具体加工

这里,让我们用相同的框架创建出其他的“产品”和“工厂”。

例如,我们这次要创建表示电视机的类Televison和表示电视机工厂的类TelevisonFactory。

这时,我们只需要引入(import)framework包就可以编写televison包。

请注意,根本没有必要修改framework包中的任何内容,就可以创建出其他的“产品”和“工厂”

请回忆一下,在framework包中我们并没有引入idcard包。在Product类和Factory类中,并没有出现IDCard和IDCardFactory等具体类的名字。

因此,使用已有的框架生成全新的类时,也完全不需要对framework进行修改,即不需要“将televison包引入到框架中”。

关于这一点,我们称作是“framework包不依赖于idcard包”。

生成实例——方法的三种实现方式

在示例程序中,Factory类的createProduct方法是抽象方法,也就是说需要在子类中实现该方法。

createProduct方法的实现方式一般有以下3种。

1.指定其为抽象方法

一旦将createProduct指定为抽象方法后,子类就必须实现该方法,否则编译错误。这也是示例程序所采用的方式。

public abstract Product createProduct(String name);

2.为其实现默认处理

实现默认处理后,如果子类没有实现该方法,将进行默认处理。

不过,这时是使用new关键字创建出实例的,因此不能将Product类定义为抽象类。

public Product createProduct(String name) {
    return new Product(name);
}

3.在其中抛出异常

createProduct方法的默认处理为抛出异常,这样如果未在子类中实现该方法,程序就会在运行时出错(报错,告知开发人员没有实现createProduct方法)。

使用模式与开发人员之间的沟通

建议在程序注释中和开发文档中记录所使用的设计模式的名称和意图,以免被其他开发同事瞎改。

相关的设计模式

Template Method 模式(第3章)

Factory Method模式是Template Method的典型应用。在示例程序中,create方法就是模板方法。

Singleton模式(第5章)

在多数情况下我们都可以将Singleton模式用于扮演Creator角色(或是ConcreteCreator角色)
的类。这是因为在程序中没有必要存在多个Creator角色(或是ConcreteCreator角色)的实例。不
过在示例程序中,我们并没有使用Singleton模式。

Composite模式(第11章)

有时可以将Composite模式用于Product 角色(或是ConcreteProduct 角色)。

Iterator模式(第1章)适应设计模式-Iterator模式

有时,在Iterator模式中使用iterator方法生成Iterator的实例时会使用Factory Method模式。

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

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

相关文章

11-树-二叉树的前序遍历

这是树的第11篇算法,力扣链接。 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 示例 1: 输入:root [1,null,2,3] 输出:[1,2,3] 做了这么久的树问题,现在开始回忆三种遍历方法,这篇文章…

C#上位机与三菱PLC的通信06--MC协议之QnA-3E报文测试

1、A-3E报文回顾 1、存储区分类及访问规则 2、命令类型 命令由主命令子命令组成 3、报文结构 2、启动mc服务器 3、创建VS项目 这节继续使用上节的VS2022的项目,增加一个方法 MCTestA3E(),具体怎么创建项目,见上节的过程。C#上位机与三菱…

Spring Security学习(六)——配置多个Provider(存在两种认证规则)

前言 《Spring Security学习(五)——账号密码的存取》一文已经能满足一般应用的情况。但实际商业应用也会存在如下的情况:用户提交的账号密码,能在本地的保存的账号密码匹配上,或者能在远端服务认证中匹配上&#xff…

171基于matlab的随机共振微弱信号检测

基于matlab的随机共振微弱信号检测,随机共振描述了过阻尼布朗粒子受周期性信号和随机噪声的共同作用下,在非线性双稳态系统中所发生的跃迁现象. 随机共振可用于弱信号的检测。程序已调通,可直接运行。

Linux用到的命令

1 压缩文件 tar -czf wonderful.tar.gz pm 这个命令的作用就是创建一个以.tar.gz结尾的包文件,然后调用gzip程序将当前目录下的pm文件夹压缩到这个以.tar.gz结尾的文件里面去

Colmap学习笔记(一):Pixelwise View Selection for Unstructured Multi-View Stereo论文阅读

1. 摘要 本文展示一套MVS系统,该系统利用非结构化的图片实现鲁棒且稠密的建模。本文的主要贡献是深度和法向量的联合估计,用光度和几何先验进行像素筛选,多视图几何一致项,该项同时进行精修和基于图片的深度和法向量的融合。在标…

C2-1.6 Dropout正则化——提高泛化能力

C2-1.6 Dropout正则化——提高泛化能力 1、参考书籍 2、什么是Dropout正则化 以图一为例: 假设在训练图一所示的神经网络,它存在过拟合(模型过于复杂情况),dropout 会遍历网络的每一层(每一层设置的阈值不…

StarRocks加速查询——低基数全局字典

前言 StarRocks-2.0引入了低基数全局字典,可以通过全局字典将字符串的相关操作转换成整型相关操作,极大提升了查询性能。StarRocks 2.0后的版本默认会开启低基数字典优化。 一、低基数字典 对于利用整型替代字符串进行处理,通常使用字典编码…

Docker之查看并获取最新Ubuntu镜像(十)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

在vue3中使用canvas实现雨滴效果

在vue3中使用canvas实现雨滴效果 这是封装的一个组件DotAndRain&#xff08; &#xff09; <script setup> import { ref, onMounted } from "vue"; import { onUnmounted } from "vue";let animationFrameId null;const el ref(null); let canv…

Java零基础 - 赋值运算符

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

redis入门指南

文章目录 Redis概述Redis基本数据类型Redis与MySQL的区别以及使用场景如何保持双写一致性&#xff08;缓存一致性&#xff09;1. 延迟双删2. 分布式锁&#xff08;强一致性时使用&#xff09;3. 中间件 Redis持久化机制RDB&#xff08;redis database&#xff09;AOF&#xff0…

Python算法100例-1.10 数制转换

完整源代码项目地址&#xff0c;关注博主私信源代码后可获取 1.问题描述2.问题分析3.算法设计4.确定程序框架5.字符与数字进行转换6.其他数制转换成十进制7.十进制转换成其他数制8.完整的程序 1&#xff0e;问题描述 给定一个M进制的数x&#xff0c;实现对x向任意一个非M进制…

SwiftUI 集合视图(Grid)拖放交换 Cell 的极简实现

概览 自从 SwiftUI 横空出世那天起&#xff0c;小伙伴们都感受到了它惊人的简单与便捷。而在本课中&#xff0c;我们将会用一个小“栗子”更直观的让大家体验到它无与伦比简洁的描述性特质&#xff1a; 如上图所示&#xff0c;我们在 SwiftUI 中实现了 Grid 中拖放交换 Cell 的…

开开开开开,干

大家新年快乐&#xff0c;开工啦啦啦啦 其实每天很多人都会问&#xff1a; 有协同过滤的算法吗&#xff0c;有的&#xff0c;可以给你解释原理… 有的小伙伴只开了一部分逻辑&#xff0c;我要实现用户可以下单功能 但是细细考虑下单&#xff0c;需要现有用户&#xff0c;维护…

[Git] 配置Access Token 解决Github 认证弹窗

[Git] 配置Access Token 解决Github 认证弹窗 1. 前言2. 解决2.1 申请Personal Access Token2.2. 配置Token2.3. 授权激活Token 博主热门文章推荐&#xff1a; 1. 前言 最近从bitbucket切换到了Github Enterprise, 刚使用几次发现 每次操作 都有弹窗认证&#xff0c; 虽然手动点…

波奇学Linux:进程通信管道

进程通信 管道&#xff1a;基于文件级别的单向通信 创建父子进程&#xff0c;使得进程的struct file*fd_array[]的文件描述符指向同一个struct file文件&#xff0c;这个文件是内存级文件。 父进程关写端&#xff0c;子进程再关闭读端。实现单向通信 子进程写入&#xff0c;父进…

个人博客搭建

使用彩虹云主机百度云域名WordPress 下载WordPress https://cn.wordpress.org/ 购买主机 购买彩虹云主机&#xff0c;购买香港高防主机https://www.cccyun.net/ 购买之后点击 管理 进入后点 击前往控制面板 -> 一键登录控制面板 可进入控制面板。 选择文件管理 在线…

cuda加速:memory coalescing,Bank Conflicts

cuda加速&#xff1a;memory coalescing 1.memory coalescing2.Shared Memory Bank Conflicts参考文献 1.memory coalescing 参考【1】中给出的定义&#xff1a;一个warp中&#xff0c;thread 0到thread 31访问连续的内存空间&#xff0c;则这些线程的访问被合并为一次访问。 …

2.21学习总结

1.【模板】ST 表 2.Balanced Lineup G 3.景区导游 4.最近公共祖先&#xff08;LCA&#xff09; 倍增思想&#xff1a;主要用于LCA问题&#xff0c;RMQ问题。在进行 递推 时&#xff0c;如果 状态空间很大&#xff0c;通常的 线性递推 无法满足 时间 与 空间复杂度 的要求&…