Java设计模式之十一 ---- 策略模式和模板方法模式
前言
在上一篇中我们学习了行为型模式的访问者模式(Visitor Pattern)和中介者模式(Mediator Pattern)。本篇则来学习下行为型模式的两个模式,策略模式(Strategy Pattern)和模板模式(Template Pattern)。
策略模式
简介
策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
策略模式主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。
- 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。
示例图如下:
这里为了方便理解,我们就拿刚学习Java的时候使用计算方法来说吧。
在使用计算器进行计算的时候,会经常用到加减乘除方法。如果我们想得到两个数字相加的和,我们需要用到“+”符号,得到相减的差,需要用到“-”符号等等。虽然我们可以通过字符串比较使用if/else写成通用方法,但是计算的符号每次增加,我们就不得不加在原先的方法中进行增加相应的代码,如果后续计算方法增加、修改或删除,那么会使后续的维护变得困难。
但是在这些方法中,我们发现其基本方法是固定的,这时我们就可以通过策略模式来进行开发,可以有效避免通过if/else来进行判断,即使后续增加其他的计算规则也可灵活进行调整。
首先定义一个抽象策略角色,并拥有一个计算的方法。
interface CalculateStrategy {
int doOperation(int num1, int num2);
}
然后再定义加减乘除这些具体策略角色并实现方法。
那么代码如下:
class OperationAdd implements CalculateStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
class OperationSub implements CalculateStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
class OperationMul implements CalculateStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
class OperationDiv implements CalculateStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 / num2;
}
}
最后在定义一个环境角色,提供一个计算的接口供客户端使用。
代码如下:
class CalculatorContext {
private CalculateStrategy strategy;
public CalculatorContext(CalculateStrategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
编写好之后,那么我们来进行测试。
测试代码如下:
public static void main(String[] args) {
int a=4,b=2;
CalculatorContext context = new CalculatorContext(new OperationAdd());
System.out.println("a + b = "+context.executeStrategy(a, b));
CalculatorContext context2 = new CalculatorContext(new OperationSub());
System.out.println("a - b = "+context2.executeStrategy(a, b));
CalculatorContext context3 = new CalculatorContext(new OperationMul());
System.out.println("a * b = "+context3.executeStrategy(a, b));
CalculatorContext context4 = new CalculatorContext(new OperationDiv());
System.out.println("a / b = "+context4.executeStrategy(a, b));
}
输出结果:
a + b = 6
a - b = 2
a * b = 8
a / b = 2
策略模式优点:
扩展性好,可以在不修改对象结构的情况下,为新的算法进行添加新的类进行实现;
灵活性好,可以对算法进行自由切换;
策略模式缺点:
使用策略类变多,会增加系统的复杂度。;
客户端必须知道所有的策略类才能进行调用;
使用场景:
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为;
一个系统需要动态地在几种算法中选择一种;
如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现;
模板模式
简介
模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
模板模式,其主要的的思想就是做一个模板,提供给客户端进行调用。除去生活中我们经常用到的简历模板、合同模板等等,Java中也有很经典的模板使用,那就是Servlet,HttpService类提供了一个service()方法,这个方法调用七个do方法中的一个或几个,完成对客户端调用的响应。这些do方法需要由HttpServlet的具体则由子类提供。
模板模式主要由抽象模板(Abstract Template)角色和具体模板(Concrete Template)角色组成。
抽象模板(Abstract Template): 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤;定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
具体模板(Concrete Template): 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤;每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
示例图如下:
这里为了方便理解,我们依旧使用一个简单的示例来加以说明。
我们以前在玩魂斗罗、双截龙、热血物语、忍者神龟等等游戏的时候,都需要在小霸王游戏机上插卡,然后启动游戏才能玩,其中魂斗罗这种游戏,启动游戏之后就可以直接玩了,但是忍者神龟这种游戏则在启动游戏之后,需要选择其中一个角色才能开始玩。那么我们可以根据这个场景写出一个通用的模板,主要包含启动游戏,玩游戏,结束游戏这几个必须实现的方法,选择人物这个方法改成可选。
那么这个抽象类的代码如下:
abstract class Game{
//启动游戏
protected abstract void runGame();
//选择人物
protected void choosePerson() {};
//开始玩游戏
protected abstract void startPlayGame();
//结束游戏
protected abstract void endPlayGame();
//模板方法
public final void play() {
runGame();
choosePerson();
startPlayGame();
endPlayGame();
}
}
定义好该抽象类之后,我们再来定义具体模板实现类。这里定义两个游戏类,一个是魂斗罗,一个忍者神龟。
那么代码如下:
class ContraGame extends Game{
@Override
protected void runGame() {
System.out.println("启动魂斗罗II...");
}
@Override
protected void startPlayGame() {
System.out.println("1P正在使用S弹打aircraft...");
}
@Override
protected void endPlayGame() {
System.out.println("1P被流弹打死了,游戏结束!");
}
}
class TMNTGame extends Game{
@Override
protected void runGame() {
System.out.println("启动忍者神龟III...");
}
@Override
protected void choosePerson() {
System.out.println("1P选择了Raph !");
}
@Override
protected void startPlayGame() {
System.out.println("Raph正在使用绝技 “火箭头槌” ");
}
@Override
protected void endPlayGame() {
System.out.println("Raph 掉进井盖里死了,游戏结束了! ");
}
}
最后再来进行测试,测试代码如下:
public static void main(String[] args) {
Game game = new ContraGame();
game.play();
System.out.println();
game = new TMNTGame();
game.play();
}
输出结果:
启动魂斗罗II...
1P正在使用S弹打aircraft...
1P被流弹打死了,游戏结束!
启动忍者神龟III...
1P选择了Raph !
Raph正在使用绝技 “火箭头槌”
Raph 掉进井盖里死了,游戏结束了!
模板模式优点:
扩展性好,对不变的代码进行封装,对可变的进行扩展;
可维护性好,因为将公共代码进行了提取,使用的时候直接调用即可;
模板模式缺点:
因为每一个不同的实现都需要一个子类来实现,导致类的个数增加,会使系统变得复杂;
使用场景:
有多个子类共有逻辑相同的方法;
重要的、复杂的方法,可以考虑作为模板方法。
注意事项:
为防止恶意操作,一般模板方法都加上 final 关键词!
Java设计模式之十一 ---- 策略模式和模板方法模式的更多相关文章
- Java设计模式之十一种行为型模式(附实例和详解)
Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J设计模式之五大创建型 ...
- Java设计模式(十一) 享元模式
原创文章,同步发自作者个人博客 http://www.jasongj.com/design_pattern/flyweight/.转载请注明出处 享元模式介绍 享元模式适用场景 面向对象技术可以很好的 ...
- Android设计模式之命令模式、策略模式、模板方法模式
命令模式是其它很多行为型模式的基础模式.策略模式是命令模式的一个特例,而策略模式又和模板方法模式都是算法替换的实现,只不过替换的方式不同.下面来谈谈这三个模式. 命令模式 将一个请求封装为一个对象,从 ...
- Java设计模式(20)观察者模式(Observer模式)
Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广泛,遵循 ...
- Java设计模式(9)适配器模式(Adapter模式)
适配器模式定义:将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份. 为何使用适配器模式 我们经常碰到要将两个没有关系的类组合在一起使用 ...
- Java设计模式(7)装饰模式(Decorator模式)
Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这 ...
- 深入探索Java设计模式(三)之装饰器模式
装饰器模式使你可以在运行时使用类似于对象组成的技术来装饰类.这在我们希望实例化具有新职责的对象而无需对基础类进行任何代码更改的情况下尤其有用.本文是在学习完优锐课JAVA架构VIP课程—[框架源码专题 ...
- 深入探索Java设计模式(四)之享元模式
享元模式适用于需要大量相同类型对象的情况.在此,设计布局可以减少创建多个对象的方式.对象在运行时会消耗资源,因此最好在内存中使用较少的对象.它减少了内存占用并利用了程序的整体性能.本文是在学习完优锐课 ...
- 简介Python设计模式中的代理模式与模板方法模式编程
简介Python设计模式中的代理模式与模板方法模式编程 这篇文章主要介绍了Python设计模式中的代理模式与模板方法模式编程,文中举了两个简单的代码片段来说明,需要的朋友可以参考下 代理模式 Prox ...
随机推荐
- apache伪静态配置(URL重写)
1.打开apache配置文件 httpd.conf .2.开启rewrite模块,去掉注释# #LoadModule rewrite_module modules/mod_rewrite 3.让apa ...
- Asp.Net MVC +EntityFramework主从表新增编辑操作的实现(删除操作怎么实现?)
Asp.Net MVC +EntityFramework主从表新增编辑操作的实现 对于MVC中同时对主从表的表单操作在网上现有的解决很少,而这样的操作在做业务系统中是经常为遇到的.我在网上搜索了很久都 ...
- shiro源码篇 - shiro的session共享,你值得拥有
前言 开心一刻 老师对小明说:"乳就是小的意思,比如乳猪就是小猪,乳名就是小名,请你用乳字造个句" 小明:"我家很穷,只能住在40平米的乳房" 老师:" ...
- php模拟post提交请求,调用接口
/** * 模拟post进行url请求 * @param string $url * @param string $param */ function request_post($url = '', ...
- T-SQL:探究UNOIN,INTERSECT,EXCEPT集合运算符(十一)
1.UNOIN运算符 unoin合并了两个输入查询结果 并消除重复项 简单点说 就是输出并集 SELECT country, region, city FROM HR.Employees UNI ...
- 【Java】HashMap源码分析——基本概念
在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...
- inheritConstructorStealing.js
// 借用构造函数 // 其基本思路是在子类型构造函数的内部调用父类型的构造函数 function Person(name){ this.name = name; this.friends = [&q ...
- Python 线程同步锁, 信号量
同步锁 import time, threading def addNum(): global num num -= 1 num = 100 thread_list = [] for i in ran ...
- 2017-11-20 中文代码示例之Vuejs入门教程(一)问题后续
"中文编程"知乎专栏原文 第一个issue: Error compiling template if using unicode naming as v-for alias · I ...
- js 中判断变量是数组还是对象,和判断对象是否为空
判断是对象还是数组 var ids={ id:'1',num:'2' } if(Array.isArray(ids) == false) {console.log('不是数组,对象') } else ...