Java设计模式透析之 —— 模板方法(Template Method)

本文介绍如何使用模板方法设计模式重构代码,以支持多种数据格式(如XML、JSON和YAML)之间的灵活转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天你还是像往常一样来上班,一如既往地开始了你的编程工作。

项目经理告诉你,今天想在服务器端增加一个新功能,希望写一个方法,能对Book对象进行处理,将Book对象的所有字段以XML格式进行包装,这样以后可以方便与客户端进行交互。并且在包装开始前和结束后要打印日志,这样方便调试和问题定位。

没问题!你觉得这个功能简直是小菜一碟,非常自信地开始写起代码。

Book对象代码如下:

[java]  view plain  copy
  1. public class Book {  
  2.   
  3.     private String bookName;  
  4.   
  5.     private int pages;  
  6.   
  7.     private double price;  
  8.   
  9.     private String author;  
  10.   
  11.     private String isbn;  
  12.   
  13.     public String getBookName() {  
  14.         return bookName;  
  15.     }  
  16.   
  17.     public void setBookName(String bookName) {  
  18.         this.bookName = bookName;  
  19.     }  
  20.   
  21.     public int getPages() {  
  22.         return pages;  
  23.     }  
  24.   
  25.     public void setPages(int pages) {  
  26.         this.pages = pages;  
  27.     }  
  28.   
  29.     public double getPrice() {  
  30.         return price;  
  31.     }  
  32.   
  33.     public void setPrice(double price) {  
  34.         this.price = price;  
  35.     }  
  36.   
  37.     public String getAuthor() {  
  38.         return author;  
  39.     }  
  40.   
  41.     public void setAuthor(String author) {  
  42.         this.author = author;  
  43.     }  
  44.   
  45.     public String getIsbn() {  
  46.         return isbn;  
  47.     }  
  48.   
  49.     public void setIsbn(String isbn) {  
  50.         this.isbn = isbn;  
  51.     }  
  52.   
  53. }  

然后写一个类专门用于将Book对象包装成XML格式:
[java]  view plain  copy
  1. public class Formatter {  
  2.   
  3.     public String formatBook(Book book) {  
  4.         System.out.println("format begins");  
  5.         String result = "";  
  6.         result += "<book_name>" + book.getBookName() + "</book_name>\n";  
  7.         result += "<pages>" + book.getPages() + "</pages>\n";  
  8.         result += "<price>" + book.getPrice() + "</price>\n";  
  9.         result += "<author>" + book.getAuthor() + "</author>\n";  
  10.         result += "<isbn>" + book.getIsbn() + "</isbn>\n";  
  11.         System.out.println("format finished");  
  12.         return result;  
  13.     }  
  14.   
  15. }  
调用代码如下:
[java]  view plain  copy
  1. public class Test {  
  2.       
  3.     public static void main(String[] args) throws Exception {  
  4.         Book book = new Book();  
  5.         book.setBookName("Thinking in Java");  
  6.         book.setPages(880);  
  7.         book.setPrice(68);  
  8.         book.setAuthor("Bruce Eckel");  
  9.         book.setIsbn("9787111213826");  
  10.         Formatter formatter = new Formatter();  
  11.         String result = formatter.formatBook(book);  
  12.         System.out.println(result);  
  13.     }  
  14.   
  15. }  

你写好了之后,迫不及待地开始运行,运行结果也完全符合你的期望。


项目经理看完后,对你非常满意,小伙效率很高的嘛!你也非常的得意。

不过两天之后,项目经理又找到了你,他说之前没有考虑到需要交互的客户端还包括手机设备,而手机设备都比较吃流量,用XML格式来传输太耗流量了,想最好能改成使用JSON格式传输。但是之前的XML格式也要保留,最好可以由客户端指定使用哪种格式。

你有些不开心,心里低估着,为什么一开始不考虑周全呢,现在又要改遗留代码。但对方毕竟是领导,你还是要服从命令的,于是你开始修改Formatter类:

[java]  view plain  copy
  1. public class Formatter {  
  2.   
  3.     public static final int XML = 0;  
  4.   
  5.     public static final int JSON = 1;  
  6.   
  7.     public String formatBook(Book book, int format) {  
  8.         System.out.println("format begins");  
  9.         String result = "";  
  10.         if (format == XML) {  
  11.             result += "<book_name>" + book.getBookName() + "</book_name>\n";  
  12.             result += "<pages>" + book.getPages() + "</pages>\n";  
  13.             result += "<price>" + book.getPrice() + "</price>\n";  
  14.             result += "<author>" + book.getAuthor() + "</author>\n";  
  15.             result += "<isbn>" + book.getIsbn() + "</isbn>\n";  
  16.         } else if (format == JSON) {  
  17.             result += "{\n";  
  18.             result += "\"book_name\" : \"" + book.getBookName() + "\",\n";  
  19.             result += "\"pages\" : \"" + book.getPages() + "\",\n";  
  20.             result += "\"price\" : \"" + book.getPrice() + "\",\n";  
  21.             result += "\"author\" : \"" + book.getAuthor() + "\",\n";  
  22.             result += "\"isbn\" : \"" + book.getIsbn() + "\",\n";  
  23.             result += "}";  
  24.         }  
  25.         System.out.println("format finished");  
  26.         return result;  
  27.     }  
  28.   
  29. }  

调用代码如下:
[java]  view plain  copy
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) throws Exception {  
  4.         Book book = new Book();  
  5.         book.setBookName("Thinking in Java");  
  6.         book.setPages(880);  
  7.         book.setPrice(68);  
  8.         book.setAuthor("Bruce Eckel");  
  9.         book.setIsbn("9787111213826");  
  10.         Formatter formatter = new Formatter();  
  11.         String result = formatter.formatBook(book, Formatter.XML);  
  12.         System.out.println(result);  
  13.         result = formatter.formatBook(book, Formatter.JSON);  
  14.         System.out.println(result);  
  15.     }  
  16.   
  17. }  
再次运行程序,得到了以下结果。


项目经理看到运行结果后开心地说:“太好了,这正是我想要的!” 

可是你这次却没有那么开心,你觉得代码已经有些混乱了,XML格式的逻辑和JSON格式的逻辑混淆在一起,非常不利于阅读,而且如果以后还需要扩展功能也会非常困难。好在传输格式一般也就XML和JSON了,应该不会再有什么扩展了,你这样安慰自己道。

但幻想总会被现实打破,“我最近听说有个YAML格式挺好玩的.......” 项目经理说道。这个时候你已经有想打人的冲动了!!!


很多时候就是这样,在公司里写的代码乱七八糟,质量极差,很大一部分原因就是因为需求变来变去。我们不断在原有代码基础上补充各种后续加入的情况,在一行行新增的if语句下面,我们的代码变得不堪入目。当然,我们作为程序员,对于需求这种东西没有太多的话语权,在这方面我们无能为力。但是我们可以尽量地把程序的架构设计好,让我们写出的代码更具有扩展性,这样就可以应对各种需求变更了。


下面你将要使用23种设计模式中的模板方法来改进以上程序。

首先将Formatter中的代码进行修改,如下所示:

[java]  view plain  copy
  1. public abstract class Formatter {  
  2.   
  3.     public String formatBook(Book book, int format) {  
  4.         beforeFormat();  
  5.         String result = formating(book);  
  6.         afterFormat();  
  7.         return result;  
  8.     }  
  9.   
  10.     protected void beforeFormat() {  
  11.         System.out.println("format begins");  
  12.     }  
  13.   
  14.     protected abstract String formating(Book book);  
  15.   
  16.     protected void afterFormat() {  
  17.         System.out.println("format finished");  
  18.     }  
  19.   
  20. }  
你会发现format_book方法只有四步,第一步调用before_format,去打印格式转换前的日志。第二步调用formating,这个方法是个抽象方法,用于处理具体的转换逻辑,因此每一个继承自Formatter的子类都需要重写此方法,来实现各自的转换逻辑。第三步调用after_format,去打印格式转换后的日志。第四步返回result。由于类中存在了抽象方法,我们也就需要把Formatter声明成抽象类。

然后要定义专门的子类来处理每种传输格式的具体逻辑,这样不同传输格式的逻辑可以从一个方法里分离开,明显便于阅读和理解。

定义类XMLFormatter继承自Formatter,里面加入处理XML格式的具体逻辑:

[java]  view plain  copy
  1. public class XMLFormatter extends Formatter {  
  2.   
  3.     @Override  
  4.     protected String formating(Book book) {  
  5.         String result = "";  
  6.         result += "<book_name>" + book.getBookName() + "</book_name>\n";  
  7.         result += "<pages>" + book.getPages() + "</pages>\n";  
  8.         result += "<price>" + book.getPrice() + "</price>\n";  
  9.         result += "<author>" + book.getAuthor() + "</author>\n";  
  10.         result += "<isbn>" + book.getIsbn() + "</isbn>\n";  
  11.         return result;  
  12.     }  
  13.   
  14. }  

定义类JSONFormatter继承自Formatter,里面加入处理JSON格式的具体逻辑:
[java]  view plain  copy
  1. public class JSONFormatter extends Formatter {  
  2.   
  3.     @Override  
  4.     protected String formating(Book book) {  
  5.         String result = "";  
  6.         result += "{\n";  
  7.         result += "\"book_name\" : \"" + book.getBookName() + "\",\n";  
  8.         result += "\"pages\" : \"" + book.getPages() + "\",\n";  
  9.         result += "\"price\" : \"" + book.getPrice() + "\",\n";  
  10.         result += "\"author\" : \"" + book.getAuthor() + "\",\n";  
  11.         result += "\"isbn\" : \"" + book.getIsbn() + "\",\n";  
  12.         result += "}";  
  13.         return result;  
  14.     }  
  15.   
  16. }  

最后调用代码如下:

[java]  view plain  copy
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) throws Exception {  
  4.         Book book = new Book();  
  5.         book.setBookName("Thinking in Java");  
  6.         book.setPages(880);  
  7.         book.setPrice(68);  
  8.         book.setAuthor("Bruce Eckel");  
  9.         book.setIsbn("9787111213826");  
  10.         XMLFormatter xmlFormatter = new XMLFormatter();  
  11.         String result = xmlFormatter.formatBook(book);  
  12.         System.out.println(result);  
  13.         JSONFormatter jsonFormatter = new JSONFormatter();  
  14.         result = jsonFormatter.formatBook(book);  
  15.         System.out.println(result);  
  16.     }  
  17.   
  18. }  

运行之后,你会发现运行结果和修改前代码的运行结果完全相同。但是使用模板方法之后,代码的可读性有了很大的提高,因为处理格式转换的代码都放到了各自的类当中,而不是全部塞进一个方法中。并且在扩展性上也有了很大的提升,比如你开始感兴趣项目经理说的YAML格式了。

定义类YAMLFormatter继承自Formatter,里面加入处理YAML格式的具体逻辑:

[java]  view plain  copy
  1. public class YAMLFormatter extends Formatter {  
  2.   
  3.     @Override  
  4.     protected String formating(Book book) {  
  5.         String result = "";  
  6.         result += "book_name: " + book.getBookName() + "\n";  
  7.         result += "pages: " + book.getPages() + "\n";  
  8.         result += "price: " + book.getPrice() + "\n";  
  9.         result += "author: " + book.getAuthor() + "\n";  
  10.         result += "isbn: " + book.getIsbn() + "\n";  
  11.         return result;  
  12.     }  
  13.   
  14. }  

调用代码只需要加入:
[java]  view plain  copy
  1. YAMLFormatter yamlFormatter = new YAMLFormatter();  
  2. String result = yamlFormatter.formatBook(book);  
  3. System.out.println(result);  

好了,令人头疼的YAML格式就这样被支持了,只需要在调用的时候决定是实例化XMLFormatter,JSONFormatter还是YAMLFormatter,就可以按照相应的规格进行格式转换了。而且整体的代码很有条理,看起来也很舒心。这个时候,你会轻松地向项目经理调侃一句,还有需要支持的格式吗?


模板方法: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

<think>嗯,用户问设计模式中有没有模板模式。我需要先回想一下设计模式的分类。设计模式通常分为创建型、结构型和行为型。模板方法模式应该属于行为型模式,对吧?是的,模板方法模式(Template Method Pattern)确实存在。 接下来,用户可能是在学习设计模式,或者在项目中遇到了相关问题,想确认是否存在这种模式。他们可能对模板模式的具体应用和结构不太清楚,需要详细的解释。要确保回答准确,避免混淆其他类似模式,比如策略模式。 用户之前讨论过未来规划,涉及医疗信息化和AI技术,可能在软件开发中需要应用设计模式来提升代码质量。因此,解释模板模式时要结合他们可能的实际应用场景,比如框架开发或流程标准化,这样更有针对性。 需要确认模板方法模式的定义、结构、应用场景以及优缺点。同时,举例时要贴近他们的领域,比如医疗系统中的数据处理流程,或者慢病管理中的统一流程框架。这样用户更容易理解,也能看到其实际价值。 还要注意用户的技术栈,他们提到Spring Boot和微服务,可能在Java环境中工作,所以示例代码使用Java会更合适。同时,对比策略模式,帮助用户区分何时使用模板方法,何时选择其他模式,这样可以避免混淆。 最后,确保回答结构清晰,分点说明,并给出实际案例,这样用户能快速掌握模板方法模式的核心要点,并应用到实际开发中。</think>是的,设计模式中确实存在**模板方法模式(Template Method Pattern)**,它是行为型设计模式的典型代表。以下为您详细解析: --- ### **1. 核心定义** **模板方法模式**通过定义一个算法框架的骨架,将某些步骤的具体实现延迟到子类中完成,使得子类可以不改变算法结构即可重新定义某些特定步骤。 --- ### **2. 模式结构** ```java // 抽象模板类 abstract class AbstractClass { // 模板方法(定义算法骨架) public final void templateMethod() { step1(); step2(); step3(); } // 具体步骤的默认实现 protected void step1() { System.out.println("默认步骤1"); } // 抽象方法(需子类实现) protected abstract void step2(); // 钩子方法(可选扩展点) protected void step3() { System.out.println("基础步骤3"); } } // 具体实现类 class ConcreteClass extends AbstractClass { @Override protected void step2() { System.out.println("自定义步骤2"); } @Override protected void step3() { super.step3(); // 可选择性覆盖 System.out.println("扩展步骤3"); } } ``` --- ### **3. 典型应用场景** #### (1) 医疗系统开发实例 - **场景**:不同医院的检验报告生成流程 ```java abstract class ReportGenerator { // 固定流程:数据获取→格式转换→签名验证→输出 public final void generateReport() { fetchData(); convertFormat(); if(needSignature()) { addDigitalSignature(); } export(); } protected abstract void convertFormat(); // 钩子方法:默认不需要签名 protected boolean needSignature() { return false; } } // 三甲医院实现类 class GradeAHospitalReport extends ReportGenerator { @Override protected void convertFormat() { System.out.println("转换为卫健委标准XML格式"); } @Override protected boolean needSignature() { return true; } } ``` #### (2) 您的血透系统应用 - **统一设备数据解析流程**:数据清洗→异常检测→持久化存储 - **慢病管理流程标准化**:患者建档→风险评估→干预方案生成 --- ### **4. 模式优势** - **流程固化**:确保核心算法不被破坏(如医疗业务流程合规性) - **扩展可控**:通过钩子方法(Hook Method)实现灵活扩展 - **减少重复**:公共代码提升至抽象类(如血透设备通信校验逻辑) --- ### **5. 与策略模式对比** | | 模板方法模式 | 策略模式 | |----------------|----------------------------------|--------------------------------| | **控制层级** | 父类控制流程 | 客户端控制算法选择 | | **扩展方式** | 通过子类继承扩展 | 通过接口实现扩展 | | **适用场景** | 算法步骤固定但部分可变 | 完整算法可替换 | | **医疗案例** | 不同医院报告生成流程 | 多种AI模型切换的透析预测系统 | --- ### **6. Spring框架中的应用** 在Spring的`JdbcTemplate`中,模板方法模式被广泛使用: ```java public class JdbcTemplate extends JdbcAccessor { public <T> T execute(ConnectionCallback<T> action) { // 固定流程:获取连接→执行回调→释放资源 Connection con = getDataSource().getConnection(); try { return action.doInConnection(con); // 关键抽象方法 } finally { releaseConnection(con); } } } ``` --- ### **7. 使用建议** - **适合场景**:当多个子类有**相同流程框架**但**部分步骤不同**时(如医疗系统不同版本的数据迁移工具) - **慎用情况**:若算法步骤频繁变化,建议改用**策略模式** - **您的技术规划结合点**:在开发血透5.0的智能报警模块时,可用模板方法统一报警处理流程(数据采集→AI分析→通知发送)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值