【java设计模式】之 模板方法(Template Method)模式

简介:

目录(?)[+]

1. 模板方法的一个实例

        这一节主要来学习一下设计模式中的模板方法模式。我们先来看一个例子:假如现在老板让你做一个汽车的模型,要求只要完成基本功能即可,不考虑扩展性,那你会怎么做呢?我们首先会根据经验设计一个类图:

       由这个类图可知,非常简单的实现了悍马车,该车有两个型号H1和H2。那现在我们开始实现这两个型号的悍马车,首先我们得把抽象类写好,然后两个不同的模型实现类通过简单的继承就可以实现要求。首先看看抽象类的代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public abstract class HummerModel {  
  2.     public abstract void start(); //发动  
  3.     public abstract void stop();  //停止  
  4.     public abstract void alarm(); //鸣笛  
  5.     public abstract void engineBoom(); //轰鸣  
  6.     public abstract void run(); //车总归要跑  
  7. }  
        简单到不行,下面我们来实现两个悍马的模型:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //悍马H1  
  2. public class HummerH1 implements HummerModel {  
  3.   
  4.     @Override  
  5.     public void start() {  
  6.         System.out.println("H1发动……");  
  7.     }  
  8.   
  9.     @Override  
  10.     public void stop() {  
  11.         System.out.println("H1停止……");  
  12.     }  
  13.   
  14.     @Override  
  15.     public void alarm() {  
  16.         System.out.println("H1鸣笛……");  
  17.     }  
  18.   
  19.     @Override  
  20.     public void engineBoom() {  
  21.         System.out.println("H1轰鸣……");  
  22.     }  
  23.   
  24.     @Override  
  25.     public void run() {  
  26.         this.start();  
  27.         this.engineBoom();  
  28.         this.alarm();  
  29.         this.stop();  
  30.     }  
  31. }  
  32.   
  33. //悍马H2  
  34. public class HummerH2 implements HummerModel {  
  35.   
  36.     @Override  
  37.     public void start() {  
  38.         System.out.println("H2发动……");  
  39.     }  
  40.   
  41.     @Override  
  42.     public void stop() {  
  43.         System.out.println("H2停止……");  
  44.     }  
  45.   
  46.     @Override  
  47.     public void alarm() {  
  48.         System.out.println("H2鸣笛……");  
  49.     }  
  50.   
  51.     @Override  
  52.     public void engineBoom() {  
  53.         System.out.println("H2轰鸣……");  
  54.     }  
  55.   
  56.     @Override  
  57.     public void run() {  
  58.         this.start();  
  59.         this.engineBoom();  
  60.         this.alarm();  
  61.         this.stop();  
  62.     }  
  63. }  
        很明显,已经发现代码有点问题了,两个悍马的run方法完全相同。所以这个run方法应该出现在抽象类中,不应该在实现类中,抽象是所有子类的共性封装。所以我们修改一下抽象类:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public abstract class HummerModel {  
  2.     public abstract void start(); //发动  
  3.     public abstract void stop();  //停止  
  4.     public abstract void alarm(); //鸣笛  
  5.     public abstract void engineBoom(); //轰鸣  
  6.     public void run() { //车总归要跑  
  7.         this.start();  
  8.         this.engineBoom();  
  9.         this.alarm();  
  10.         this.stop();  
  11.     }  
  12. }  
        这样两个实现类就不用实现run方法了,可以直接拿来用。其实,这就是模板方法模式。

2. 模板方法模式的定义

        模板方法模式很简单,它的定义是:Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses define certain steps of an algorithm without changing the algorithm's structure. 即定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可冲定义该算法的某些特定步骤。模板方法模式的通用类图如下:

        模板方法模式确实很简单,仅仅使用了Java的继承机制,但是它是一个应用非常广泛的模式,其中AbstractClass叫做抽象模板,它的方法分为两类:基本方法(由子类去实现)和模板方法(可以有一个或多个,也就是一个框架,实现对基本方法的调度,完成固定的逻辑)。为了防止恶意的操作,一般模板方法上都添加上final关键字,不允许被覆写。我们来看一下AbstractClass模板:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public abstract class AbstractClass {  
  2.     //基本方法  
  3.     protected abstract void doSomething();  
  4.     protected abstract void doAnything();  
  5.     //模板方法  
  6.     public void templateMethod() {  
  7.         //调用基本方法,完成相关的逻辑  
  8.         this.doAnything();  
  9.         this.doSomething();  
  10.     }  
  11. }  
        具体实现类就不写了……

3. 模板方法模式的优缺点

      优点:

        1)封装不变部分,扩展可变部分:把认为不变部分的算法封装到父类实现,可变部分则可以通过继承来实现,很容易扩展。

        2)提取公共部分代码,便于维护:上面悍马的例子就是个很好的解释。

        3)行为由父类控制,由子类实现。

      缺点:

        模板方法模式颠倒了我们平常的设计习惯:抽象类负责声明最抽象、最一般的事物属性和方法,实现类实现具体的事物属性和方法。在复杂的项目中可能会带来代码阅读的难度。

4. 模板方法模式的扩展

        还是上面那个悍马的例子,现在老板说这车干嘛跑起来就要鸣笛,太吵了,难道不是应该让用户决定它是否要鸣笛么?好像确实是这样的……那好办,我们可以修改一下抽象模板类中的方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public abstract class HummerModel {  
  2.     protected abstract void start(); //发动  
  3.     protected abstract void stop();  //停止  
  4.     protected abstract void alarm(); //鸣笛  
  5.     protected abstract void engineBoom(); //轰鸣  
  6.     final public void run() { //车总归要跑  
  7.         this.start();  
  8.         this.engineBoom();  
  9.         if(this.isAlarm()) {//想让它叫就叫,不想就不叫        
  10.             this.alarm();  
  11.         }  
  12.         this.stop();  
  13.     }  
  14.     protected boolean isAlarm() { //我们加了一个判断方法,默认返回true  
  15.         return true;  
  16.     }  
  17. }  
       我们在模板类中增加了一个判断方法来判断是否要鸣笛,现在就好办了,具体实现类只要重写这个方法就可以做到人为控制是否要鸣笛了,下面我们来看一下实现类:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class HummerH1 extends HummerModel {  
  2.   
  3.     private boolean alarmFlag = true//判断标记  
  4.     @Override  
  5.     public void start() {  
  6.         System.out.println("H1发动……");  
  7.     }  
  8.   
  9.     @Override  
  10.     public void stop() {  
  11.         System.out.println("H1停止……");  
  12.     }  
  13.   
  14.     @Override  
  15.     public void alarm() {  
  16.         System.out.println("H1鸣笛……");  
  17.     }  
  18.   
  19.     @Override  
  20.     public void engineBoom() {  
  21.         System.out.println("H1轰鸣……");  
  22.     }  
  23.       
  24.     @Override  
  25.     protected boolean isAlarm() { //覆写isAlarm方法,返回判断标记  
  26.         return this.alarmFlag;  
  27.     }  
  28.       
  29.     public void setAlarm(boolean isAlarm) { //设置判断标记  
  30.         this.alarmFlag = isAlarm;  
  31.     }  
  32.       
  33. }  
        这个实现很好,我们在实现类中定义一个判断标记,然后对外提供一个public接口setAlarm来让外界设置这个判断标记,这就像是开关一样,想让它ture和false都行。这个isAlarm方法俗称 钩子方法。有了钩子方法的模板方法模式才算完美,大家可以想象一下,由子类的一个方法返回值决定公共部分的执行结果,这个是很有吸引力的。我们来测试一下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class Test {  
  2.     public static void main(String[] args) throws IOException {  
  3.         System.out.println("----H1型悍马----");  
  4.         System.out.println("是否需要喇叭声响? 0-不需要  1-需要");  
  5.         String type = new BufferedReader(new InputStreamReader(System.in)).readLine();  
  6.         HummerH1 h1 = new HummerH1();  
  7.         if(type.equals("0")) {  
  8.             h1.setAlarm(false);  
  9.         }  
  10.         h1.run();  
  11.     }  
  12. }  

        当输入不同的指令后,就会决定不同的动作:即要不要鸣笛,至此,这个模板方法模式就介绍完了。

        相关阅读:http://blog.csdn.net/column/details/des-pattern.html

_____________________________________________________________________________________________________________________________________________________

-----乐于分享,共同进步!

-----更多文章请看:http://blog.csdn.net/eson_15

相关文章
|
4天前
|
存储 Java 开发者
【Java新纪元启航】JDK 22:解锁未命名变量与模式,让代码更简洁,思维更自由!
【9月更文挑战第7天】JDK 22带来的未命名变量与模式匹配的结合,是Java编程语言发展历程中的一个重要里程碑。它不仅简化了代码,提高了开发效率,更重要的是,它激发了我们对Java编程的新思考,让我们有机会以更加自由、更加创造性的方式解决问题。随着Java生态系统的不断演进,我们有理由相信,未来的Java将更加灵活、更加强大,为开发者们提供更加广阔的舞台。让我们携手并进,共同迎接Java新纪元的到来!
27 11
|
7天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
12天前
|
Java
用JAVA架建List集合为树形结构的代码方法
这段代码定义了一个表示树形结构的 `Node` 类和一个用于构建树形结构的 `TreeController`。`Node` 类包含基本属性如 `id`、`pid`、`name` 和 `type`,以及子节点列表 `children`。`TreeController` 包含初始化节点列表并将其转换为树形结构的方法。通过过滤和分组操作实现树形结构的构建。详情可见:[代码示例链接1](http://www.zidongmutanji.com/zsjx/43551.html),[代码效果参考链接2](https://www.257342.com/sitemap/post.html)。
25 5
|
12天前
|
消息中间件 Java
【实战揭秘】如何运用Java发布-订阅模式,打造高效响应式天气预报App?
【8月更文挑战第30天】发布-订阅模式是一种消息通信模型,发送者将消息发布到公共队列,接收者自行订阅并处理。此模式降低了对象间的耦合度,使系统更灵活、可扩展。例如,在天气预报应用中,`WeatherEventPublisher` 类作为发布者收集天气数据并通知订阅者(如 `TemperatureDisplay` 和 `HumidityDisplay`),实现组件间的解耦和动态更新。这种方式适用于事件驱动的应用,提高了系统的扩展性和可维护性。
29 2
|
14天前
|
安全 Java 开发者
【技术咖必看】Java异常处理新境界:throws关键字,打造万无一失的方法签名!
【技术咖必看】Java异常处理新境界:throws关键字,打造万无一失的方法签名!
29 3
|
14天前
|
安全 Java 程序员
【程序猿逆袭指南】Java高手的秘密武器:throws关键字,让你的方法签名霸气侧漏!
【程序猿逆袭指南】Java高手的秘密武器:throws关键字,让你的方法签名霸气侧漏!
12 3
|
12天前
|
小程序 Java
【aspose-words】Aspose.Words for Java模板语法详细剖析
本文通过详细分析Aspose.Words for Java模板语法,介绍了使用条件块、变量和动态合并表格单元格三个常用模板标签,并结合实际案例进行演示。通过这三个标签的实操,帮助读者更好地掌握Aspose.Words的使用技巧。此外,还提供了官方文档链接以便进一步学习。
55 0
【aspose-words】Aspose.Words for Java模板语法详细剖析
|
15天前
|
Java Spring 容器
Java获取接口的所有实现类方法
这篇文章介绍了在Java中获取接口所有实现类的方法,包括使用JDK的ServiceLoader(SPI机制)和Spring Boot中的@Autowired自动注入及ApplicationContextAware接口两种方式。
37 1
|
16天前
|
监控 Java API
提升 Java 后台性能的十大方法
提升 Java 后台性能的十大方法
29 2
|
18天前
|
安全 Java 应用服务中间件
【Azure 应用服务】App Service中,为Java应用配置自定义错误页面,禁用DELETE, PUT方法
【Azure 应用服务】App Service中,为Java应用配置自定义错误页面,禁用DELETE, PUT方法
【Azure 应用服务】App Service中,为Java应用配置自定义错误页面,禁用DELETE, PUT方法
下一篇
DDNS