一、概念

1、概念

模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式

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

2、举例理解

网上举了一个请客吃饭的例子,我觉得解释的挺好的。我们每个人去请客吃饭。一般都含三个步骤:点单吃东西买单,而且顺序就是从做左到右的。

在这三个步骤中,点单和买单大同小异,最大的区别在于第二步——吃什么?吃面条和吃满汉全席可大不相同,如图1所示

所以从开发角度来分析,有时也会遇到类似的情况,某个方法的实现需要多个步骤(类似“ 请客 ”),其中有些步骤是固定的(类似“点单”和“买单”),而有些步骤并不固定,存在

可变性(类似“吃东西”)。

为了提高代码的复用性和系统的灵活性,我们把"点单"和"买单"的实现放在父类中实现,而对于"吃东西",因为差异性就很大所以在父类中只做一个声明,将其具体实现放在不同的

子类中,在一个子类中提供“吃面条”的实现,而另一个子类提供“吃满汉全席”的实现。通过使用模板方法模式,一方面提高了代码的复用性,另一方面还可以利用面向对象的多态性,

在运行时选择一种具体子类,实现完整的“请客”方法,提高系统的灵活性和可扩展性。

3、结构和说明

AbstractClass:抽象类。用来定义算法骨架和原语操作,在这个类里面,还可以提供算法中通用的实现

ConcreteClass:具体实现类。用来实现算法骨架中的某些步骤,完成跟特定子类相关的功能。

public abstract class AbstractClass {

    /**
* 1、点餐 直接用final修饰,代表子类不能在重写
*/
private final void order(){
//点餐
}
/**
* 2、吃东西 吃什么由子类实现
*/
public abstract void eatSomething();
/**
* 3、结算
*/
private final void settlement(){
//结算
}
/**
* 记录一次请客
*/
protected final void workOneDay()
{
//点单
order();
//吃东西
eatSomething();
//结单
settlement();
} /**
* 是否需要上厕所,这个其实可以理解成一个钩子,意思就是子类可以选择是否重写,不重写就用父类的方法。
* 在有些时候 可以通过重写修改boolean的返回值,可以调协一些流程。
*/
protected boolean isNeedWc()
{
return false;
}
}

子类这里就不写了。

4、优缺点

优点

  • 封装不变,扩展可变:父类封装了具体流程以及实现部分不变行为,其它可变行为交由子类进行具体实现;
  • 流程由父类控制,子类进行实现:框架流程由父类限定,子类无法更改;子类可以针对流程某些步骤进行具体实现;

缺点

抽象规定了行为,具体负责实现,与通常事物的行为相反,会带来理解上的困难(通俗地说,“父类调用了子类方法”);

5、应用场景

  • 多个子类有公有的方法,并且逻辑基本相同时;
  • 重要,复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现;
  • 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类,然后通过钩子函数约束其行为;

二、模版方法模式实战示例

之前在参与一个app后段开发,里面有个资讯模块。这个模版的内容不是我们工作人员进行编辑,而是通过爬虫去获取各大网站的资讯,然后处理好后存入我们的数据库。

之前大概爬了十几个网站的资讯。这里的业务流程是这样的 1、爬取资讯内容->2、校验是否已经抓取过->3、存储内容。

这里只有第一步是需要子类去实现的,因为每个网站的数据格式都是不一样的,所以我们需要子类爬取后,统一处理成我们的格式。那么第二步第三步是可以通过父类完成的。

这里代码如下。

1、抽象类

抽象类

/**
* 定义一个爬取网站资讯的模版
*/
@Slf4j
public abstract class AbstractCrawlNewsService { /**
* 1、爬取各网站消息 由子类去实现
*/
protected abstract List<Object> crawlPage(int pageNum) throws IOException; /**
* 2、校验该资讯是否已经爬取过 因为这个逻辑是一样的 由父类实现就可以了
*/
protected final Map<String, Boolean> isCrawled(List<Object> checkParams){
//数据库校验
return new HashMap<>();
} /**
* 3、保存资讯 同样由父类实现即可
*/
protected final void saveArticle(Object object){
//保存数据库
} /**
* 模版方法 定义了上面的一整套流程
*/
protected void doTask(String url) {
int pageNum = 1;
while (true) {
//1、爬取网站数据,因为网站不可能一次性获取所以资讯数据的 所以进行分页查询 默认第一页开始
List<Object> newsList = crawlPage(pageNum++);
// 抓取不到新的内容本次抓取结束
if (CollectionUtils.isEmpty(newsList)) {
break;
}
//2、校验是否已经抓取过滤(查询我们自己数据库)
Map<String, Boolean> crawledMap = isCrawled(newsList); //3、将数据保存数据库
for (int i = newsList.size() - 1; i >= 0; i--) {
Object object = newsList.get(i);
// 没有爬取过,才进行爬取
if (!crawledMap.getOrDefault(object.getTitle(), false)) {
saveArticle(object);
}
}
//可以考虑请求后休眠以下 因为太频繁IP容易被封。
ThreadUtils.sleep(2);
}
}
}

2、具体实现类

接口

/**
* @Description: 爬取获悉网接口
*/
public interface CrawlerHuoXingService { void start();
}

实现类

/**
* 抓取火星网新闻
*/
@Slf4j
@Service
public class CrawlerHuoXingServiceImpl extends AbstractCrawlNewsService
implements CrawlerHuoXingService { /**
* 爬取接口
*/
@Override
protected List<Object> crawlPage(int pageNum) throws IOException {
//通过url获取指定网站接口 进行爬取
return new ArrayList<>();
} @Override
public void start() {
try {
doTask("http://www.huoxing24.com/news");
} catch (IOException e) {
log.error("抓取火星网新闻异常", e);
}
}
}

至于什么时候去爬取资讯,我们可以通过定时器,定时去爬取。

定时任务类

/**
* @Description: 定时爬取火星网资讯
*/
@Slf4j
@Component
public class ScheduleHuoXingTrigger { @Autowired
private CrawlerHuoXingService crawlerHuoXingService; /**
* 定时抓取火星资讯
*/
@Scheduled(initialDelay = 1000, fixedDelay = 15 * 60 * 1000)
public void doCrawlHuoXing() { try {
crawlerHuoXingService.start();
} catch (Exception e) {
log.error("本次抓取火星资讯异常", e);
}
}
}

整个大致流程就是这样,以后要是添加一条资讯那就只要写多个具体实现类就行。

参考

1、设计模式 模版方法模式 展现程序员的一天

2、模板方法模式深度解析(一)

【java设计模式】(10)---模版方法模式(案例解析)的更多相关文章

  1. JS常用的设计模式(10)——模版方法模式

    模式方法是预先定义一组算法,先把算法的不变部分抽象到父类,再将另外一些可变的步骤延迟到子类去实现.听起来有点像工厂模式( 非前面说过的简单工厂模式 ). 最大的区别是,工厂模式的意图是根据子类的实现最 ...

  2. JAVA设计模式之模版方法模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述模板方法(Template Method)模式的: 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式 ...

  3. java设计模式之模版方法模式以及在java中作用

    模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有 ...

  4. Java设计模式之工厂方法模式(转) 实现是抽象工厂?

    Java设计模式之工厂方法模式 责任编辑:覃里作者:Java研究组织   2009-02-25   来源:IT168网站   文本Tag: 设计模式 Java [IT168 技术文章]         ...

  5. Java设计模式系列-工厂方法模式

    原创文章,转载请标注出处:<Java设计模式系列-工厂方法模式> 一.概述 工厂,就是生产产品的地方. 在Java设计模式中使用工厂的概念,那就是生成对象的地方了. 本来直接就能创建的对象 ...

  6. Chapter 10 模版方法模式

    我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模版模式来处理. 模版方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模 ...

  7. 设计模式 笔记 模版方法模式 Template Method

    //---------------------------15/04/28---------------------------- //TemplateMethod 模版方法模式----类行为型模式 ...

  8. 设计模式之模版方法模式(Template Method Pattern)

    一.什么是模版方法模式? 首先,模版方法模式是用来封装算法骨架的,也就是算法流程 既然被称为模版,那么它肯定允许扩展类套用这个模版,为了应对变化,那么它也一定允许扩展类做一些改变 事实就是这样,模版方 ...

  9. Java设计模式 之 工厂方法模式

    1. 使用设计模式的好处:可提高代码的重复性,让代码更容易被他人理解,保证代码的可靠性. 2. 工厂模式定义:就是创建一个工厂类来创建你需要的类,工厂模式包括工厂模式和抽象工厂模式,抽象工厂模式是工厂 ...

  10. java设计模式(二)---工厂方法模式

    2普通工厂方法模式 就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 2.1创建接口 /** * 发送接口 * Created by mrf on 2016/2/25. */ public ...

随机推荐

  1. 【PHP数据结构】图的存储结构

    图的概念介绍得差不多了,大家可以消化消化再继续学习后面的内容.如果没有什么问题的话,我们就继续学习接下来的内容.当然,这还不是最麻烦的地方,因为今天我们只是介绍图的存储结构而已. 图的顺序存储结构:邻 ...

  2. PHP的OpenSSL加密扩展学习(一):对称加密

    我们已经学过不少 PHP 中加密扩展相关的内容了.而今天开始,我们要学习的则是重点中的重点,那就是 OpenSSL 加密扩展的使用.为什么说它是重点中的重点呢?一是 OpenSSL 是目前 PHP 甚 ...

  3. PHP中命名空间是怎样的存在?(三)

    这是与命名空间有关的最后一篇.最后还是两个比较简单的内容,是关于命名空间和全局相关的一些类.函数.常量的使用对比.当然,最后我们还会总结一下命名空间的名称解析规则做为这三篇系列文章的结束. 全局空间 ...

  4. 如何解决SVN Upgrade working copy问题

    电脑还原系统后,安装了最新版本的SVN,发现原来在svn检出的文件夹出现了SVN Upgrade working copy,没有commit ,没有update. 在网上查询到:出现这个的原因是因为你 ...

  5. Expression Tree 遍历集合

    场景 从接口返回的数据是集合,却是 object 类型的.这个时候需要遍历这个集合.现提供两种方法. 方法一: 因为集合是可枚举的,所以可以尝试转为 IEnumerable 类型,然后遍历即可. st ...

  6. 鸿蒙内核源码分析(线程概念篇) | 是谁在不停的折腾CPU? | 百篇博客分析OpenHarmony源码 | v21.06

    百篇博客系列篇.本篇为: v21.xx 鸿蒙内核源码分析(线程概念篇) | 是谁在不断的折腾CPU | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  7. AT4505-[AGC029F]Construction of a tree【构造题,hall定理,网络流】

    正题 题目链接:https://www.luogu.com.cn/problem/AT4505 题目大意 给出\(n\)个点和\(n-1\)个点集\(U_i\),每个点集中选择两个点连边使得该图是一棵 ...

  8. ThreadLocal底层

    1. 首先我们来看一下他的使用 public class ThreadLocalTest { public static void main(String[] args) { MyThread thr ...

  9. 巧用优先队列:重载运算符在STL优先队列中的应用

    前言 写优先队列优化dijkstra的时候,需要放进优先队列的常常有数值和编号两类,以下介绍让编号捆绑数值放入优先队列的几种方法. 由于过程比较简单,记住代码即可,下面不再讲解,只附上代码,请读者自行 ...

  10. 题解 [CTSC2006]歌唱王国

    题目传送门 Desctiption 见题面. Solution 人类智慧... 考虑这样一个赌博游戏,现在有一个猴子,它随机从 \(1\sim n\) 中选一个打出来.现在有若干个赌徒,他们一开始都有 ...