行为型设计模式(一)模版方法模式 迭代器模式

模板方法模式 Template

1、什么是模版方法模式

模版方法模式定义了一个算法的骨架,它将其中一些步骤的实现推迟到子类里面,使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。

2、为什么使用模版方法模式

  1. 封装不变部分:模版方法模式将算法的不变部分封装在父类中,使得子类只需要实现变化的部分,提高了代码的复用性。
  2. 扩展性:子类可以通过重写父类的方法来扩展或修改算法的行为,提高了灵活性。
  3. 避免代码重复:将相同的代码放在父类中,避免了在每个子类中重复相同的代码。

3、如何使用模版方法模式

设计实现一个制作咖啡的场景,其中一些步骤是相同的,而一些不同的步骤可以由子类重写

// 模板类
abstract class CoffeeTemplate {
    // 模板方法,定义咖啡的制作步骤
    final void makeCoffee() {
        boilWater();
        brewCoffeeGrounds();
        pourInCup();
        addCondiments();
    }

    // 具体步骤,煮沸水
    void boilWater() {
        System.out.println("Boiling water");
    }

    // 具体步骤,冲泡咖啡
    void brewCoffeeGrounds() {
        System.out.println("Brewing coffee grounds");
    }

    // 具体步骤,倒入杯中
    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 具体步骤,添加调料,子类实现
    abstract void addCondiments();
}

// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
    @Override
    void addCondiments() {
        System.out.println("Adding lemon");
    }
}

// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
    @Override
    void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        CoffeeTemplate tea = new TeaTemplate();
        tea.makeCoffee();

        CoffeeTemplate coffee = new CoffeeTemplateImpl();
        coffee.makeCoffee();
    }
}

4、是否存在缺陷和不足

  1. 限制子类:模版方法模式可能限制了子类的灵活性,因为在这个模式中要求子类必须遵循父类定义的算法骨架。
  2. 难以维护:如果模版方法变得复杂,可能会导致难以维护和理解。

5、如何缓解缺陷和不足

  1. 使用钩子方法:在模版方法中引入钩子方法,允许子类选择性地实现或者覆盖某些步骤,提高灵活性。
// 模板类
abstract class CoffeeTemplate {
    // 模板方法,定义咖啡的制作步骤
    final void makeCoffee() {
        boilWater();
        brewCoffeeGrounds();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    // 具体步骤,煮沸水
    void boilWater() {
        System.out.println("Boiling water");
    }

    // 具体步骤,冲泡咖啡
    void brewCoffeeGrounds() {
        System.out.println("Brewing coffee grounds");
    }

    // 具体步骤,倒入杯中
    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 具体步骤,添加调料,子类实现
    abstract void addCondiments();

    // 钩子方法,决定是否添加调料,默认添加
    boolean customerWantsCondiments() {
        return true;
    }
}

// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
    @Override
    void addCondiments() {
        System.out.println("Adding lemon");
    }

    // 通过重写钩子方法,决定是否添加调料
    @Override
    boolean customerWantsCondiments() {
        // 用户不想要调料
        return false;
    }
}

// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
    @Override
    void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        CoffeeTemplate tea = new TeaTemplate();
        tea.makeCoffee();

        CoffeeTemplate coffee = new CoffeeTemplateImpl();
        coffee.makeCoffee();
    }
}
// 使用钩子方法 customerWantsCondiments 来判断是否执行添加调料的步骤,然后在 TeaTemplate 中
// 重写钩子方法,用户可以选择是否添加。CoffeeTemplateImpl 中没有重写钩子方法,就默认执行
  1. 使用策略模式:考虑将某些步骤设计成策略对象,允许在运行时切换算法的实现。
// 策略接口,调料的添加策略
interface CondimentsStrategy {
    void addCondiments();
}

// 具体的调料策略,添加柠檬
class LemonCondiments implements CondimentsStrategy {
    @Override
    public void addCondiments() {
        System.out.println("Adding lemon");
    }
}

// 具体的调料策略,添加糖和牛奶
class SugarAndMilkCondiments implements CondimentsStrategy {
    @Override
    public void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

// 模板类
abstract class CoffeeTemplate {
    // 策略对象,用于调料的添加
    private CondimentsStrategy condimentsStrategy;

    // 设置调料策略
    void setCondimentsStrategy(CondimentsStrategy condimentsStrategy) {
        this.condimentsStrategy = condimentsStrategy;
    }

    // 模板方法,定义咖啡的制作步骤
    final void makeCoffee() {
        boilWater();
        brewCoffeeGrounds();
        pourInCup();
        addCondiments();
    }

    // 具体步骤,煮沸水
    void boilWater() {
        System.out.println("Boiling water");
    }

    // 具体步骤,冲泡咖啡
    void brewCoffeeGrounds() {
        System.out.println("Brewing coffee grounds");
    }

    // 具体步骤,倒入杯中
    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 具体步骤,添加调料,通过策略对象调用
    void addCondiments() {
        if (condimentsStrategy != null) {
            condimentsStrategy.addCondiments();
        }
    }
}

// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
    // 构造方法中设置具体的调料策略
    TeaTemplate() {
        setCondimentsStrategy(new LemonCondiments());
    }
}

// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
    // 构造方法中设置具体的调料策略
    CoffeeTemplateImpl() {
        setCondimentsStrategy(new SugarAndMilkCondiments());
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        CoffeeTemplate tea = new TeaTemplate();
        tea.makeCoffee();

        CoffeeTemplate coffee = new CoffeeTemplateImpl();
        coffee.makeCoffee();
    }
}
// 引入 CondimentsStrategy 策略接口和具体的策略实现类:LemonCondiments 和 SugarAndMilkCondiments
// 如此将调料的添加变成一个可变的部分。在 CoffeeTemplateImpl 中引入策略对象,通过
// setCondimentsStrategy 设置具体的调料策略
  1. 分解大方法:如果模版方法变得复杂,考虑将其分解成多个小方法,使得每个方法都相对简单。
// 模板类
abstract class CoffeeTemplate {
    // 策略对象,用于调料的添加
    private CondimentsStrategy condimentsStrategy;

    // 设置调料策略
    void setCondimentsStrategy(CondimentsStrategy condimentsStrategy) {
        this.condimentsStrategy = condimentsStrategy;
    }

    // 模板方法,定义咖啡的制作步骤
    final void makeCoffee() {
        boilWater();
        brewCoffeeGrounds();
        pourInCup();
        addCondiments();
    }

    // 具体步骤,煮沸水
    void boilWater() {
        System.out.println("Boiling water");
    }

    // 具体步骤,冲泡咖啡
    void brewCoffeeGrounds() {
        System.out.println("Brewing coffee grounds");
    }

    // 具体步骤,倒入杯中
    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 具体步骤,添加调料,通过策略对象调用
    void addCondiments() {
        if (condimentsStrategy != null) {
            condimentsStrategy.addCondiments();
        }
    }
}

// 具体子类,制作茶
class TeaTemplate extends CoffeeTemplate {
    // 构造方法中设置具体的调料策略
    TeaTemplate() {
        setCondimentsStrategy(new LemonCondiments());
    }

    // 具体步骤,添加茶叶
    void addTeaLeaves() {
        System.out.println("Adding tea leaves");
    }
}

// 具体子类,制作咖啡
class CoffeeTemplateImpl extends CoffeeTemplate {
    // 构造方法中设置具体的调料策略
    CoffeeTemplateImpl() {
        setCondimentsStrategy(new SugarAndMilkCondiments());
    }

    // 具体步骤,添加咖啡粉
    void addCoffeePowder() {
        System.out.println("Adding coffee powder");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        TeaTemplate tea = new TeaTemplate();
        tea.makeCoffee();
        tea.addTeaLeaves();

        CoffeeTemplateImpl coffee = new CoffeeTemplateImpl();
        coffee.makeCoffee();
        coffee.addCoffeePowder();
    }
}

迭代器模式 Iterator

1、什么是迭代器模式

迭代器模式定义了一种方法来顺序访问一个容器对象中的各个元素,而不需要暴露该对象的内部细节,把对元素的访问和遍历从容器对象中分离出来,使得容器和迭代器可以独立地变化。

2、为什么使用迭代器模式

  1. 分离集合与遍历:迭代器模式将集合对象的遍历行为封装到迭代器中,使得集合与遍历的关注点分离。
  2. 简化集合接口:迭代器提供了一个统一的遍历接口,简化了集合类的接口,使得集合的实现更加简洁。
  3. 支持多种遍历方法:通过不同的迭代器实现,可以支持不同的遍历方法,比如正序、逆序、过滤等。

3、如何使用迭代器模式

设计实现一个集合类来实现迭代器模式

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

// 迭代器接口
interface MyIterator {
    boolean hasNext();
    Object next();
}

// 集合接口
interface MyCollection {
    MyIterator createIterator();
}

// 具体迭代器实现
class MyListIterator implements MyIterator {
    private List<Object> list;
    private int index = 0;

    MyListIterator(List<Object> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        return index < list.size();
    }

    @Override
    public Object next() {
        if (hasNext()) {
            return list.get(index++);
        }
        return null;
    }
}

// 具体集合实现
class MyListCollection implements MyCollection {
    private List<Object> list = new ArrayList<>();

    // 添加元素
    void addElement(Object element) {
        list.add(element);
    }

    // 创建迭代器
    @Override
    public MyIterator createIterator() {
        return new MyListIterator(list);
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        MyListCollection collection = new MyListCollection();
        collection.addElement("Element 1");
        collection.addElement("Element 2");
        collection.addElement("Element 3");

        // 使用迭代器遍历集合
        MyIterator iterator = collection.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

4、是否存在缺陷和不足

迭代器模式不适用于所有的集合,主要是集合内部结构不方便直接使用迭代器的场景。

5、如何缓解缺陷和不足

  1. 使用增强的 for 循环:对于一些简单的集合,可以使用增强的 for 循环替代迭代器,代码更加简洁。
  2. 考虑其他遍历方式:如果迭代器不适用于某些集合,可以考虑其他遍历方式,比如通过索引访问元素。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Home Assistant HAOS版如何安装HACS

环境&#xff1a; Home Assistant 11.2 SSH & Web Terminal 17.0 问题描述&#xff1a; Home Assistant HAOS版如何安装HACS 解决方案&#xff1a; 1.打开WEB 里面的终端输入下面命令 wget -O - https://hacs.vip/get | bash -如果上面的命令执行后卡住不动&#xff…

得物-Golang-记一次线上服务的内存泄露排查

1.出现内存泄漏 1.1 事发现场 在风和日丽的一天&#xff0c;本人正看着需求、敲着代码&#xff0c;展望美好的未来。突然收到一条内存使用率过高的告警。 1.2 证人证词 告警的这个项目&#xff0c;老代码是python的&#xff0c;最近一直在go化。随着go化率不断上升&#xff…

maven 项目导入异常问题

问题如下 一、 tomcat正再运行的包是哪一个 不同构建、打包情况下分别运行 out\artifacts下 当直接去Project Structure下去构建artifacts 后&#xff0c;运行tomcat 则会在out下target下 reimport项目后,则会在artifacts自动生成部署包。删除tomcat之前deployment 下的包(同…

Android studio Android SDK下载安装

我们访问地址 https://developer.android.google.cn/studio?hlzh-cn 拉下来直接点击下载 然后来下来 勾选 然后点击下载 下载好之后 我们双击打开 点击下一步 确认上面的勾选 然后下一步 这里 我们选择一下安装目录 然后点击下一步 安装 安装完之后点击进行下一步 Fin…

JDK各个版本新特性

JDK8新特性 Java 8 发布于 2014 年 3 月份&#xff0c;可以说是 Java 6 之后最重要的版本更新&#xff0c;深受开发者的喜爱。 函数式编程和 Lambda 表达式 Stream 流 参考&#xff1a;https://mp.weixin.qq.com/s/7hNUjjmqKcHDtymsfG_Gtw 单从“Stream”这个单词上来看&…

【数据结构】栈的使用|模拟实现|应用|栈与虚拟机栈和栈帧的区别

目录 一、栈(Stack) 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1. 改变元素的序列 2. 将递归转化为循环 3. 括号匹配 4. 逆波兰表达式求值 5. 出栈入栈次序匹配 6. 最小栈 1.5 概念区分 一、栈(Stack) 1.1 概念 栈&#xff1a;一种特殊的线性表&…

解决腾讯云CentOS 6硬盘空间不足问题:从快照到数据迁移

引言&#xff1a; 随着数据的不断增加&#xff0c;服务器硬盘空间不足变成了许多运维人员必须面对的问题。此主机运行了httpd&#xff08;apache服务&#xff09;&#xff0c;提供对外web访问服务,web资源挂载在**/data/wwwroot目录下,http日志存放在/data/wwwlogs目录下&…

创建型设计模式 | 原型模式

一、原型模式 1、原理 原型模式&#xff0c;用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象&#xff0c;而且不需要知道任何创建的细节。原型像是一个模板&#xff0c;可以基于它复制好多…

el-form与el-upload结合上传带附件的表单数据(前端篇)

1.写在之前 本文前端采用Vue element-plus技术栈&#xff0c;前端项目参考yudao-ui-admin-vue3项目与Geeker-Admin项目。 这篇文章是el-form与el-upload结合上传带附件的表单数据&#xff08;后端篇&#xff09;-CSDN博客姐妹篇&#xff0c;后端篇文章主要讲的是后端的实现逻…

左移运算符(<<),右移运算符(>>)

#include <stdio.h>int main() {int a 10, b 10, c -10, d -10;int a1 0, a2 0, a3 0, a4 0, a5 0; int b1 0, b2 0, b3 0, b4 0, b5 0; int c1 0, c2 0, c3 0, c4 0, c5 0; int d1 0, d2 0, d3 0, d4 0, d5 0;//a10,左移a1 a << 1; a2…

JDK各个版本特性讲解-JDK14特性

JDK各个版本特性讲解-JDK14特性 一、Java14概述二、语法层面的变化1. instanceof2. switch表达式3. 文本块的改进4. Records记录类型 二、关于GC1.G1的NUMA内存分配优化2. 弃用SerialCMS,ParNewSerial Old3.删除CMS4.ZGC on macOS and Windows 三、其他变化1.友好的空指针异常提…

【动态规划】08路径问题_下降路径最小和_C++(medium)

题目链接&#xff1a;leetcode下降路径最小和 目录 题目解析&#xff1a; 算法原理 1.状态表示 2.状态转移方程 3.初始化 4.填表顺序 5.返回值 编写代码 题目解析&#xff1a; 题目让我们求通过 matrix 的下降路径 的 最小和 由题可得&#xff1a; 在下一行选择的元…

纸白银投资开户有什么条件?

在金融市场中&#xff0c;白银作为一种重要的贵金属&#xff0c;一直以来都备受投资者的关注。而纸白银&#xff0c;作为白银投资的一种形式&#xff0c;更是因其交易灵活、风险相对较小的特点&#xff0c;吸引了大量的投资者。那么&#xff0c;纸白银投资开户有什么条件呢&…

国产Apple Find My认证芯片哪里找,伦茨科技ST17H6x芯片可以帮到您

深圳市伦茨科技有限公司&#xff08;以下简称“伦茨科技”&#xff09;发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家&#xff0c;该平台提供可通过Apple Find My认证的Apple查找&#xff08;Find My&#xff09;功能集成解决方案。…

从 MySQL 到 DolphinDB,Debezium + Kafka 数据同步实战

Debezium 是一个开源的分布式平台&#xff0c;用于实时捕获和发布数据库更改事件。它可以将关系型数据库&#xff08;如 MySQL、PostgreSQL、Oracle 等&#xff09;的变更事件转化为可观察的流数据&#xff0c;以供其他应用程序实时消费和处理。本文中我们将采用 Debezium 与 K…

VBA之Word应用:利用代码统计文档中的书签个数

《VBA之Word应用》&#xff08;版权10178982&#xff09;&#xff0c;是我推出第八套教程&#xff0c;教程是专门讲解VBA在Word中的应用&#xff0c;围绕“面向对象编程”讲解&#xff0c;首先让大家认识Word中VBA的对象&#xff0c;以及对象的属性、方法&#xff0c;然后通过实…

Jenkins+Docker+Gitee搭建自动化部署平台

目录 服务器准备 Docker安装 yum 包更新到最新 设置yum源 安装docker 启动和开机启动 验证安装是否成功 Jenkins安装 拉取镜像 创建映射目录 运行镜像 运行出错 修正权限 重新运行镜像 新建安全组&#xff0c;放通8080端口 激活Jenkins Jenkins插件 Jenkins全…

GitHub 如何修改 Fork from

如果你的仓库上面是 Fork from 的话&#xff0c;我们有什么办法能够取消掉这个 Fork from&#xff1f; 解决办法 GitHub 上面没有让你取消掉 Fork 的办法。 如果进入设置&#xff0c;在可见设置中也没有办法修改仓库的可见设置选项。 唯一的解决办法就是对你需要修改的仓库先…

景区气象站:旅游体验的新升级

随着科技的发展和人们生活水平的提高&#xff0c;越来越多的人选择在节假日或周末外出旅游&#xff0c;感受大自然的美好。然而&#xff0c;在享受大自然的同时&#xff0c;天气因素成为了影响旅游体验的关键因素之一。为了更好地服务游客&#xff0c;许多景区开始引入气象站&a…

Java 基础学习(十四)Map集合与Set集合

1 Map集合 1.1 Map接口 1.1.1 Map接口概述 Map接口是一种双列集合。Map的每个元素都包含一个键对象Key和一个值对象Value &#xff0c;键对象和值对象之间存在对应关系&#xff0c;这种关系称为映射&#xff08;Mapping&#xff09;。 Map接口中的元素&#xff0c;可以通过…