云社区 博客 博客详情

设计模式——模板方法模式

yd_221104950 发表于 2020-12-04 00:45:55 2020-12-04
0
0

【摘要】 模板方法模式定义 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况,重新定义算法中的某些步骤。 类图: AbstractClass:这个抽象类包含了模板方法。模板方法执行了一系列的算法(方法)。模板方法本身和抽象方法的具体实现之间被解耦了。 ConcreteClass:这个具体类实现了抽象的操作,当模板方法需要...

模板方法模式定义

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况,重新定义算法中的某些步骤。
类图:

在这里插入图片描述

AbstractClass:这个抽象类包含了模板方法。模板方法执行了一系列的算法(方法)。模板方法本身和抽象方法的具体实现之间被解耦了。
ConcreteClass:这个具体类实现了抽象的操作,当模板方法需要这两个抽象方法时,会调用实现它们的子类。

模板方法是用来创建一个算法的模板。模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这样可以确保算法的结构保持不变,同时由子类提供部分实现

模板方法定义了一连串的步骤,每个步骤由一个方法代表。
我们还可以有“默认不做事的方法”,我们称这种方法为“hook”(钩子)。子类可以视情况决定要不要覆盖它们。
“hook”(钩子)是一种被声明在抽象类中的方法。但它只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩(这是事先将钩子放在了某个点上)。要不要挂钩,由子类自行决定。

如果要使用钩子,就必须在子类中覆盖它。
对于抽象父类中的方法,我们这样考虑:当我们的子类“必须”提供算法中某个方法或步骤的实现时,就声明为抽象方法;如果算法的这个部分是可选的,就声明为钩子(一个空的或有默认实现的方法)。如果是钩子的话,子类可以选择实现这个钩子,但并不强制这么做。当某些步骤是可选的,我们可以将这些步骤设计成钩子,而不是抽象方法。这样可以让抽象类的子类的负荷减轻。

我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你”。低层组件挂载上系统的点,是高层模块事先决定的。当我们设计模板方法时,告诉子类“不要调用我们,我们会调用你“。

模板方法模式是一个很常见的模式,因为对创建框架来说,由框架控制如何做事情,而由你(使用这个框架的人)指定框架算法中每个步骤的细节。

使用钩子的实例


import javax.swing.*;
import java.awt.*;

public class MyFrame extends JFrame { public MyFrame(String title){ super(title); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300,300); this.setVisible(true); } public void paint(Graphics graphics){ super.paint(graphics); String  msg = "I rule!"; graphics.drawString(msg,100,100); }


}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

上面这个实例,我们扩展了JFrame,它包含一个update()方法,这个方法控制更新屏幕的算法。我们可以通过覆盖paint()钩子方法和这个算法挂上钩。
JFrame的更新算法被称为paint()。在默认状态下,paint()方法是不做事的…它只是一个钩子。我们覆盖paint(),是为了让JFrame在容器上面画出一条消息。

测试:

public class Main { public static void main(String[] args) { MyFrame myFrame = new MyFrame("Hello world"); }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

测试结果会弹出一个窗口。
还有大量的applet使用钩子来提供行为。因为这些行为是作为钩子实现的,所以Applet类就不用去实现它们。

我们再来看看一个例子,再次说明模板方法模式是怎么用的。我们以煮茶与煮咖啡为例,它们两者都涉及煮水、倒水(倒茶或倒咖啡)。不同的是冲泡的过程和添加的配料不同,茶要泡,配料可加糖或柠檬或不加,咖啡要冲再过滤,可以加牛奶或糖或不加:
1.共同的部分写在父类AbstractClassCaffeineBeverageWithHook中,不同部分(异变部分)用抽象方法表示,由子类来实现,父类要提供一个模板方法封装冲茶或冲咖啡的每一个过程(算法步骤),如下面这个父类中的prepareRecipe方法:

package abstract_class;

public abstract class AbstractClassCaffeineBeverageWithHook { final public  void prepareRecipe(){ boilWater(); brew(); pourInCup(); if(customerWantsCondiments()){ addCondiments(); } } protected abstract void brew(); protected abstract  void addCondiments(); public void boilWater(){ System.out.println("Boiling water"); } public void pourInCup(){ System.out.println("Pouring water"); } //在这里定义了一个方法,通常是空的缺省实现。这个方法只会返回true,不做别的事 //这是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做 protected boolean customerWantsCondiments(){ return true; }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

2.子类
CoffeeWithHook

package concrete_class;

import abstract_class.AbstractClassCaffeineBeverageWithHook;

public class CoffeeWithHook extends AbstractClassCaffeineBeverageWithHook { @Override protected void brew() { System.out.println("Dripping Coffee through filter"); } @Override protected void addCondiments() { System.out.println("Add sugar and Milk"); } @Override protected boolean customerWantsCondiments() { //覆盖这个钩子,提供自己的功能,不返回true了,返回false。即不加任何东西 return  false; }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

TeaWithNoHook,它不重要钩子,即默认要加配料:

package concrete_class;

import abstract_class.AbstractClassCaffeineBeverageWithHook;

public class TeaWithNoHook extends AbstractClassCaffeineBeverageWithHook { @Override protected void brew() { System.out.println("steeping the tea"); } @Override protected void addCondiments() { System.out.println("Add lemon"); }

}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3.测试

import abstract_class.AbstractClassCaffeineBeverageWithHook;
import concrete_class.CoffeeWithHook;
import concrete_class.TeaWithNoHook;

public class Main { public static void main(String[] args) {
// MyFrame myFrame = new MyFrame("Hello world"); AbstractClassCaffeineBeverageWithHook coffee = new CoffeeWithHook(); coffee.prepareRecipe(); AbstractClassCaffeineBeverageWithHook tea = new TeaWithNoHook(); tea.prepareRecipe(); }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4.测试结果

Boiling water
Dripping Coffee through filter
Pouring water
Boiling water
steeping the tea
Pouring water
Add lemon


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

小结:

  • “模板方法”定义了算法的步骤,把这些步骤的实现延迟到子类。
  • 模板方法模式为我们提供了一种代码复用的重要技巧。
  • 模板方法的抽象类可以定义具体方法、抽象方法和钩子。
  • 抽象方法由子类实现。
  • 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它。
  • 为了防止子类改变模板方法中的算法,可以将模板方法声明为final。
  • 将决策权放在高层模块中,以便决定如何以及何时调用低层模块。
  • 策略模式和模板方法模式都封装算法,一个用组合,一个用继承。
  • 工厂方法是模板方法的一种特殊版本。

demo示例代码
谢谢阅读

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/88944435

登录后可下载附件,请登录或者注册

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:huaweicloud.bbs@huawei.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
评论文章 //点赞 收藏 0
点赞
分享文章到微博
分享文章到朋友圈

上一篇:WebView JS与app本地方法互相调用

下一篇:设计模式——命令模式

评论 (0)


登录后可评论,请 登录注册

评论