当前位置: 首页 > news >正文

设计模式之模版方法

模版方法介绍

模版方法(Template Method)模式是一种行为型设计模式它定义了一个操作(模板方法)的基本组合与控制流程,将一些步骤(抽象方法)推迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。这种设计方式将特定步骤的具体实现与操作流程分离开来,实现了代码的复用和扩展,从而提高代码质量和可维护性。

1、模版方法模式的定义与原理

模版方法模式原始定义是:在操作中定义算法的框架,将一些步骤推迟到子类中。模版方法让子类在不改变算法结构的情况下重新定义算法的某些步骤。这里的算法可以理解为广义上的业务逻辑,并不是特指某一个实际的算法。

模版方法模式的主要原理包括:

  • 抽象父类:定义一个算法所包含的所有步骤,并提供一些通用的方法逻辑。
  • 具体子类:继承自抽象父类,根据需要重写父类提供的算法步骤中的某些步骤。

2、模版方法模式的角色

模版方法模式包含以下主要角色:

  • 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
  • 模板方法(Template Method):定义了算法的骨架,按某种顺序调用其包含的基本方法。
  • 基本方法(Concrete Method):在抽象类中已经实现的方法,为算法的各个步骤提供默认实现。
  • 抽象方法(Abstract Method):在抽象类中声明,由具体子类实现的方法,用于算法的某些特定步骤。
  • 钩子方法(Hook Method):在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。钩子方法提供了算法框架中的扩展点,允许子类在不改变算法结构的情况下,通过重写这些方法来自定义算法的行为。

3、模版方法模式的优点与缺点

优点
  1. 提高代码复用性:所有的子类可以复用父类中提供的模板方法代码。
  2. 提高扩展性:框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
  3. 明确算法结构:通过模板方法,可以清晰地定义算法的框架和步骤,使得算法的结构更加明确和易于理解。
缺点
  1. 类数量增加:由于每个算法都需要一个抽象类和具体子类来实现,因此在操作流程比较多时可能导致类的数量急剧增加,从而导致代码的复杂性提高。
  2. 关联性高:模板方法与子类实现的抽象方法紧密相关,如果该模板方法需要修改,可能会涉及到多个子类的修改。

4、模版方法模式的应用场景

模版方法模式适用于以下场景:

  • 当多个类有共同的算法结构,但具体的实现步骤可能有所不同时。
  • 当需要在不改变算法结构的情况下,对算法的某些步骤进行定制时。
  • 当需要提高代码的复用性和扩展性时。

例如,在软件开发中,经常需要处理各种业务逻辑流程,这些流程通常具有相似的结构但具体的实现步骤可能因业务需求的不同而有所差异。此时,可以使用模版方法模式来定义这些流程的框架和步骤,然后通过不同的子类来实现具体的业务逻辑。这样可以减少代码的重复,提高代码的可维护性和可扩展性。

二、模版方法实现例子

以下是一个用Java编写的模版方法模式的例子。在这个例子中,我们将创建一个抽象类Game,它定义了一个游戏的基本流程(即模版方法),包括初始化游戏、玩游戏和结束游戏等步骤。然后,我们创建两个具体的游戏类VideoGameBoardGame,它们分别实现了这些步骤中的特定行为。

// 抽象游戏类  
abstract class Game {  // 模版方法,定义了游戏的流程  final void play() {  initializeGame();  while (!gameOver()) {  playStep();  }  endGame();  }  // 抽象方法,需要子类实现  abstract void initializeGame();  // 抽象方法,需要子类实现  abstract void playStep();  // 抽象方法,判断游戏是否结束,可以提供一个默认实现,也可以留空让子类实现  abstract boolean gameOver();  // 钩子方法,可以在需要时由子类重写  void endGame() {  System.out.println("Game Over!");  }  
}  // 视频游戏类  
class VideoGame extends Game {  @Override  void initializeGame() {  System.out.println("Initializing Video Game...");  }  @Override  void playStep() {  System.out.println("Playing Video Game Step...");  // 这里可以添加更多的游戏步骤逻辑  }  @Override  boolean gameOver() {  // 这里简化为总是返回true以结束游戏  // 在实际游戏中,这里应该包含判断游戏是否结束的逻辑  return true;  }  // 可以选择重写钩子方法以提供自定义的结束游戏逻辑  // 但在这个例子中,我们使用父类的默认实现  
}  // 桌游类  
class BoardGame extends Game {  @Override  void initializeGame() {  System.out.println("Setting up Board Game...");  }  @Override  void playStep() {  System.out.println("Playing Board Game Turn...");  // 这里可以添加更多的游戏回合逻辑  }  @Override  boolean gameOver() {  // 这里简化为总是返回true以结束游戏  // 在实际游戏中,这里应该包含判断游戏是否结束的逻辑  return true;  }  // 同样,可以选择重写钩子方法  
}  // 客户端类  
public class TemplateMethodPatternDemo {  public static void main(String[] args) {  Game videoGame = new VideoGame();  System.out.println("Playing Video Game:");  videoGame.play();  Game boardGame = new BoardGame();  System.out.println("\nPlaying Board Game:");  boardGame.play();  }  
}

在这个例子中,Game类定义了一个游戏的模版方法play(),它按照初始化游戏、玩游戏步骤(循环直到游戏结束)、结束游戏的顺序来执行。initializeGame()playStep()gameOver()是抽象方法,需要由子类来实现具体的游戏逻辑。endGame()是一个钩子方法,它提供了一个默认的实现,但子类可以根据需要重写它。

VideoGameBoardGame类分别实现了Game类的抽象方法,以提供各自的游戏逻辑。在main方法中,我们创建了VideoGameBoardGame的实例,并调用了它们的play()方法来玩游戏。由于play()方法是final的,因此它不能被子类重写,这保证了游戏流程的一致性。然而,通过重写抽象方法和钩子方法,子类可以灵活地实现自己的游戏逻辑。

如果觉得不错,记得点赞收藏,你们的反馈是我不断创作的动力。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 从零开始实现大语言模型(一):概述
  • uniapp 请求封装
  • JavaScript高级程序设计(第四版)--学习记录之代理与反射
  • 完美解决ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: NO)
  • Perl语言入门到高级学习
  • 【教程】Github Page 添加自定义域名
  • SQL Server和Oracle数据库的实时同步
  • python通过pyinstaller库进行打包,运行时提示缺少ODBC驱动
  • STM32智能仓库管理系统教程
  • 原创作品—数据可视化大屏
  • pytest系列——pytest_runtest_makereport钩子函数获取测试用例执行结果
  • TIA博途与威纶通触摸屏无实物仿真调试的具体方法示例
  • 用Vue3和Plotly.js绘制交互式3D散点图
  • 新浪API系列:支付API打造无缝支付体验,畅享便利生活(3)
  • python库 - modelscope
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • @jsonView过滤属性
  • Android系统模拟器绘制实现概述
  • centos安装java运行环境jdk+tomcat
  • cookie和session
  • Electron入门介绍
  • Golang-长连接-状态推送
  • input的行数自动增减
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java|序列化异常StreamCorruptedException的解决方法
  • JavaWeb(学习笔记二)
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Mac转Windows的拯救指南
  • mysql外键的使用
  • Node + FFmpeg 实现Canvas动画导出视频
  • ReactNative开发常用的三方模块
  • Ruby 2.x 源代码分析:扩展 概述
  • SQLServer之索引简介
  • Tornado学习笔记(1)
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 搞机器学习要哪些技能
  • 七牛云假注销小指南
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 原生JS动态加载JS、CSS文件及代码脚本
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • !!java web学习笔记(一到五)
  • #stm32驱动外设模块总结w5500模块
  • #stm32整理(一)flash读写
  • #前后端分离# 头条发布系统
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (C11) 泛型表达式
  • (k8s中)docker netty OOM问题记录
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (论文阅读30/100)Convolutional Pose Machines
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (十六)视图变换 正交投影 透视投影
  • (四)Linux Shell编程——输入输出重定向
  • (四)库存超卖案例实战——优化redis分布式锁