模板方法及策略设计模式实践
一、前言
最近两周工作比较忙,一直疏忽了写博客这件事。但是再忙也得坚持下去,虽然很难,但是自己定下的小目标含着泪也要把它做下去啊~~好了,废话不多说直接进入正题吧。
设计模式相信大家应该都有接触过,并且在不经意间也使用过一些设计模式,使用设计模式最重要的一点是让我们的代码看起来更清晰可读,并且更具可扩展性,还可以达到代码最大程度复用的目标。所以今天我就来介绍一下最近在项目中用到的两个设计模式,这两个设计模同样也是使用的比较多的两个模式:模板方法模式以及策略模式。
二、模板方法模式
模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。模板方法模式需要开发抽象类和具体子类。抽象类给出一个算法的轮廓和骨架,具体类则负责给出这个算法的各个各个特定逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。
模板方法所代表的行为称为顶级行为,其逻辑称为顶级逻辑。模板方法模式的静态结构图如下所示:
这里涉及到两个角色:
抽象模板(Abstract Template)角色有如下责任:
■ 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
■ 定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
具体模板(Concrete Template)角色又如下责任:
■ 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
■ 每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
有句名言说的好:Talk is cheap. Show me the code…拿出代码才有说服力嘛!那么我们就以炒菜这件事为例来实现一下模板方法模式吧。首先我们来梳理一下,如果要做一个菜需要做些什么事项,以及这些事项的顺序。俗话说,巧妇难为无米之炊。。。那最先肯定需要原材料,所以就需要去买菜;买到菜之后就是考验动手能力的时候了,需要洗菜,切菜以及做菜了。通过我个人总结,这个世界上的所有菜都有个基本操作顺序:买菜-->洗菜-->准备菜-->做菜-->出锅,所有菜的做法都是大概就是这个抽象的流程。因此,我们可以构建一个抽象类来定义这些流程,代码如下:
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 炒菜抽象类(定义炒菜的具体步骤) 5 */ 6 public abstract class AbstractCook { 7 8 protected String dishName = ""; 9 10 public void fryProcess() { 11 // 设置菜名 12 this.setDishName(); 13 // 步骤一: 买菜 14 this.buyDishes(); 15 // 步骤二: 洗菜 16 this.washDishes(); 17 // 步骤三: 准备菜 18 this.prepareDisher(); 19 // 步骤四: 做菜 20 this.fryDishes(); 21 // 步骤五: 出锅 22 this.finishDishes(); 23 } 24 25 // 设置菜名 26 protected abstract void setDishName(); 27 28 // 买菜通用步骤 29 protected void buyDishes() { 30 System.out.println("去菜市场买:" + dishName); 31 } 32 33 // 洗菜通用步骤 34 protected void washDishes() { 35 System.out.println("将" + dishName + "用水洗干净"); 36 } 37 38 // 准备菜(各类菜准备方式不一样,此处使用抽象方法) 39 protected abstract void prepareDisher(); 40 41 // 做菜(各类菜做法不一样,此处抽象) 42 protected abstract void fryDishes(); 43 44 // 出锅 45 protected void finishDishes() { 46 System.out.println("将" + dishName + "盛出并装盘"); 47 } 48 49 }
在这里我们规定了做菜的通用流程,像买菜、洗菜以及出锅这些步骤,所有菜的做法基本都是一样的,所以可以将其方法的实现定义在抽象类中,如有特别需要也可对相应步骤进行重写;而准备菜和做菜这两个步骤,不同类型的才的做法差异较大,所以需要定义为抽象方法,让具体的菜做法实现类去实现这两个步骤。接下来就是定义具体的菜的做法类,我这边给出了三个具有代表性的菜类的做法,分别是:炒青菜、蒸鸡蛋以及烧排骨,下面具体来看一下他们的实现:
1、 炒青菜
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 炒青菜类 5 */ 6 public class CookGreens extends AbstractCook { 7 @Override 8 protected void setDishName() { 9 this.dishName = "青菜"; 10 } 11 12 @Override 13 protected void prepareDisher() { 14 System.out.println("开始摘" + dishName + "..."); 15 System.out.println("开始切" + dishName + "..."); 16 System.out.println("开始对" + dishName + "进行过水..."); 17 System.out.println("开始准备蒜瓣, 并将其切碎..."); 18 } 19 20 @Override 21 protected void fryDishes() { 22 System.out.println("倒油并烧开..."); 23 System.out.println("倒入蒜瓣爆香..."); 24 System.out.println("倒入" + dishName + "进行翻炒3分钟..."); 25 } 26 }
实现了准备菜和做菜这两个抽象方法,内容对应青菜的做法。
2、 蒸鸡蛋
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 蒸鸡蛋类 5 */ 6 public class CookEggs extends AbstractCook { 7 @Override 8 protected void setDishName() { 9 this.dishName = "鸡蛋"; 10 } 11 12 @Override 13 protected void washDishes() { 14 // 鸡蛋不需要清洗,这里用一个空方法重写覆盖 15 } 16 17 @Override 18 protected void prepareDisher() { 19 System.out.println("打破" + dishName + "壳,并放入碗中..."); 20 System.out.println("捣碎" + dishName + "蛋黄和蛋清, 搅拌至均匀..."); 21 System.out.println("倒入一定量的清水以及油和盐..."); 22 } 23 24 @Override 25 protected void fryDishes() { 26 System.out.println("在蒸锅中放入一定量水..."); 27 System.out.println("放入隔层..."); 28 System.out.println("放入准备好的" + dishName + ", 盖上锅盖蒸10到15分钟..."); 29 } 30 }
实现蒸鸡蛋的准备方法与做法,另外鸡蛋不需要洗,所以把洗菜这个方法实现成了一个空的方法,表示这个菜是不需要清洗的。
3、 烧排骨
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 烧排骨类 5 */ 6 public class CookRibs extends AbstractCook{ 7 @Override 8 protected void setDishName() { 9 this.dishName = "排骨"; 10 } 11 12 @Override 13 protected void prepareDisher() { 14 System.out.println("剁碎洗干净后的" + dishName + "..."); 15 System.out.println("准备一定量的葱姜蒜,并切碎..."); 16 System.out.println("将准备好的" + dishName + "在锅中进行焯水处理后盛出..."); 17 } 18 19 @Override 20 protected void fryDishes() { 21 System.out.println("在锅中倒入油并烧开..."); 22 System.out.println("倒入准备好的葱姜蒜爆香..."); 23 System.out.println("放入焯水后的排骨进行翻炒5到10分钟..."); 24 System.out.println("倒入一定量料酒,及其他调料,继续翻炒几分钟..."); 25 } 26 }
实现了排骨的具体做法。
以上便是模板方法模式的具体实现;下面继续介绍另外一个常用的设计模式:策略模式,策略模式经常和模板方法模式结合起来使用,模板方法规定主流程,具体的实现分别对应不同的策略。
三、策略模式
策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。下图为策略模式具体结构:
这个模式涉及到三个角色:
● 环境(Context)角色:持有一个Strategy的引用。
● 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
● 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
还是以上述做菜的例子为例说明,做菜的抽象流程类便是抽象策略角色,每一种菜的实现都可以当做是一个具体策略角色,然后我们还需要加上一个环境角色,来完善这个策略模式,下面代码示例即为环境角色类:
1 package com.guigui.common.patterns.strategy; 2 3 import com.guigui.common.patterns.template.AbstractCook; 4 5 public class Context { 6 // 持有具体策略对象 7 private AbstractCook cookDish; 8 // 构造方法,传入具体策略对象 9 public Context(AbstractCook cookDish) { 10 this.cookDish = cookDish; 11 } 12 // 执行策略方法 13 public void strategyProcess(){ 14 cookDish.fryProcess(); 15 } 16 }
这样便完成了策略模式的基本结构。下面写一个示例来演示一下模板方法模式和策略模式结合起来使用过程,以下为测试类代码:
1 package com.guigui.common.patterns; 2 3 import com.guigui.common.patterns.template.AbstractCook; 4 import com.guigui.common.patterns.template.CookEggs; 5 import com.guigui.common.patterns.template.CookGreens; 6 import com.guigui.common.patterns.template.CookRibs; 7 import com.guigui.common.patterns.strategy.Context; 8 9 import java.util.HashMap; 10 import java.util.Map; 11 12 public class StrategyMain { 13 private static Map<String, AbstractCook> strategyMap = new HashMap<>(); 14 static { 15 strategyMap.put("greens", new CookGreens()); 16 strategyMap.put("eggs", new CookEggs()); 17 strategyMap.put("ribs", new CookRibs()); 18 } 19 public static void main(String[] args) { 20 // 炒青菜 21 Context green_context = new Context(strategyMap.get("greens")); 22 green_context.strategyProcess(); 23 System.out.println("=========================================="); 24 25 // 蒸鸡蛋 26 Context egg_context = new Context(strategyMap.get("eggs")); 27 egg_context.strategyProcess(); 28 System.out.println("=========================================="); 29 30 // 烧排骨 31 Context rib_context = new Context(strategyMap.get("ribs")); 32 rib_context.strategyProcess(); 33 System.out.println("=========================================="); 34 } 35 }
这个示例通过使用策略模式来做了三个菜,并且这三个菜的做法是通过一个模板方法来限定基本流程,而不同的做菜类则是不同的做菜策略,只需要将具体的策略实例传入环境角色,便可以实现对应的做菜流程。
测试结果:
好了,今天要介绍的内容大概就是这么多,有什么不对的地方欢迎大家指正,非常感谢~~~
转载于:https://www.cnblogs.com/guishenyouhuo/p/10072897.html
模板方法及策略设计模式实践相关推荐
- javascript设计模式实践之模板方法--具有百叶窗切换图片效果的JQuery插件(二)...
在上一篇<javascript设计模式实践之迭代器--具有百叶窗切换图片效果的JQuery插件(一)>里,通过采用迭代器模式完成了各初始化函数的定义和调用. 接下来就要完成各个切换效果的编 ...
- 设计模式:策略设计模式
一.什么是策略设计模式 1.1 策略设计模式定义 策略设计模式(Strategy Pattern)是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以让算法 ...
- 创建型设计模式:模板设计模式/观察者设计模式/策略设计模式
目录 设计模式的设计原则 模板设计模式 观察者模式 策略设计模式 设计模式的设计原则 依赖倒置:高层模块不应该依赖低层模块,两者都应该依赖抽象: 抽象不应该依赖具体实现,具体实现应该依赖于抽象: (记 ...
- Kubernetes(K8s)容器设计模式实践案例 – 分散收集模式
<Kubernetes与云原生应用>专栏是InfoQ向轻元科技首席架构师王昕约稿的系列 文章.本专栏包含8篇内容,将会从介绍和分析Kubernetes系统以及云原生应用 入手,逐步推出基于 ...
- vue each_Vue 应用单元测试的策略与实践 05 - 测试奖杯策略
本文首发于 Vue 应用单元测试的策略与实践 05 - 测试奖杯策略 | 吕立青的博客 欢迎关注知乎专栏 -- 前端的逆袭(凡可 JavaScript,终将 JavaScript.) 欢迎关注我的博客 ...
- java中策略设计模式_Java中的设计模式(五):策略模式
策略设计模式是行为设计模式之一.当我们为特定任务使用多个算法时,使用策略模式,客户端决定在运行时使用的实际实现. 策略模式的最佳示例之一是Collections.sort()采用Comparator参 ...
- Java设计模式之策略设计模式
1.什么是-策略设计模式 在软件开发中常常遇到这种情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Ha ...
- java策略设计模式_Java中的策略设计模式
java策略设计模式 策略设计模式是一种行为模式,其中我们有多种算法/策略来完成一项任务,所使用的算法/策略留给客户选择. 各种算法选项封装在单独的类中. 在本教程中,我们将学习在Java中实现策略设 ...
- 策略模式示例代码_策略设计模式示例
策略模式示例代码 本文是我们名为" Java设计模式 "的学院课程的一部分. 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们. 您将了解模式如此重要的 ...
最新文章
- 贪心,POJ(2709)
- 判断用户是否存在再进行新增_基于tableau从商业分析模型角度对业务数据进行多维度分析--【商业分析类】...
- java 输入流关闭顺序_JAVA的节点流和处理流以及流的关闭顺序
- 华为云数据库携新品惊艳亮相2019华为全联接大会
- html5怎么给图片添加背景透明,javascript – 透明图像背景html5画布
- JSK-398 字符$出现的次数【入门】
- 返回0-9直接的随机数
- SAPJCO3升级3.1后报错java.lang.UnsatisfiedLinkError: sapjco3.dll解决
- springboot-cache缓存和J2cache二级缓存框架(带点漫画)
- 文件共享锁溢出 请增加MaxLocksperFile注册表项值
- python3爬虫实战一: 爬取豆瓣最新上映电影及画出词云分布
- 在Arduino和ESP32-s2环境下,测试WiFi Fine Time Measurement (FTM) Round Trip Time (RTT) 的定位和测距
- 90%程序员是这样写注释的...网友:精辟
- 网络安全——Burpsuite
- excel熵值法计算权重_SPSS主成分分析 | 权重计算amp;极差法标准化超详细教程!(下)...
- 逆谐波滤波matlab,基于MATLAB仿真的SPWM逆变电路谐波分析及滤波器设计
- Pycharm 报错 Environment location directory is not empty的解决方法
- cocos creator 如何制作九宫格抽奖
- Linux搭建迅搜( Xunsearch )
- eclipse如何修改字体