headfirst设计模式(1)—策略模式
什么是策略模式
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化(摘自百度百科)
关键字:算法封装,相互替换,独立变化
算法封装表示,每个算法只提供接口,屏蔽实现的细节。相互替换很好理解,就是有一个共同的父类,当然父类不一定就是class,也可能是interface,这个要根据不同的业务场景来考虑。
独立变化怎么理解呢?这个就要牵扯到设计原则(我新来的,不知道专业术语是不是这个),对扩展开放,对修改关闭(开闭原则),简单来说就是,变化的东西放一边让它自己慢慢变化,而已经稳定的东西在扩展功能的时候不会产生变化,当然这个是良好的设计。如果加一个功能,然后发现以前的功能各种不能用了,那就太蛋疼了(不要问我怎么知道的,不然我就不会来写设计模式了)。
在软件设计中,唯一不变的东西就是:变化。你可能觉得我在扯淡,但是确实是变化,你们的产品经理有没有天天给你说,昨天那个能不能给我改一下,我觉得这样更好,balabalabala...,要不然就是,客户说上次那个功能不行,要再加点东西...,不要问我怎么知道的。
所以为什么要学设计模式呢,为了适应变化,为了少加班,这个是看得见的好处。还有一个好处是,优雅的代码可以让强迫症患者心理很舒服。
举个栗子
某公司有一款模拟鸭子的游戏,具体的表现就是,游戏中有各种鸭子,一边游泳一边叫。系统的内部采用的是标准的OO设计。
鸭子的超类如下:
public abstract class Duck {
public Duck(){
}
public abstract void display();
public void swim(){
System.out.println("所有的鸭子都会游泳");
}
public void quack(){
System.out.println("所有的鸭子都会呱呱叫");
}
}
具体实现类如下:
public class MallardDuck extends Duck {
@Override
public void display() {
System.out.println("绿头鸭,头是绿色的");
}
}
public class RedheadDuck extends Duck {
@Override
public void display() {
System.out.println("红头鸭,头是红色的");
}
}
其他实现省略....
需求变动:鸭子必须要能飞
简单的修改
在超类中新增fly方法,让所有的鸭子都具备飞行的能力
public abstract class Duck {
//其他方法省略
public void fly(){
System.out.println("所有的鸭子都会飞");
}
}
产生的问题:有的鸭子本身不能飞,但是在Duck类中添加了fly方法,却错误的赋予了它们飞行的能力,比如,橡皮鸭,超类中新加的方法,会影响所有子类的行为。
简单的解决办法:最简单的解决办法就是利用方法的复写@Override,复写fly方法,根据不同鸭子的特性去实现不同的fly。(缺陷:每新加一种鸭子,可能就会去复写超类的方法,如,加入一只木头鸭,它可能既不会飞行,又能发出声音,而且随着种类越多,那么复写的次数越多,重复的代码也就会越多,后期维护的时候可能就需要同时改很多个地方)
设计原则:应用中可能会产生变化之处,要把它们独立出来,不要和不需要变化的代码放在一处(不变化的代码一般是稳定的,加入变化的代码之后很可能就会破坏原来的稳定性)
独立变化的修改
变化的部分:飞行行为,鸣叫行为
//飞行行为
public interface FlyBehavior {
void fly();
}
//鸣叫行为
public interface QuackBehavior {
void quack();
}
Duck超类修改
public abstract class Duck { // 接口的权限要根据实际项目来权衡,这里由于子类并不需要关心这两个接口的作用
// 全部都交给了父类来调用,所以用private,屏蔽掉细节
private FlyBehavior flyBehavior;
private QuackBehavior quackBehavior;
public abstract void display();
// 关于构造函数要不要传入fly行为和quack
// 这个就要分情况来考虑,如果业务上可以提供默认的行为,那么就可以使用无参的构造函数
// 如果业务上不能提供默认行为,就可以使用以下注释中的形式,要求子类强制实现,给后面的开发人员少挖点坑(当然,他们也可以去看以前的代码)
public Duck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
this.flyBehavior = flyBehavior;
this.quackBehavior = quackBehavior;
}
// 一般来说,超类的行为是很少会出现变化的(增加或者减少),如果一直出现变化,就证明这个超类的设计有问题,
// 或者说压根就不应该用策略模式,所以上述构造函数一般是不会调整的
// 在调用过程中一般不要出现下面这种写法,吞掉错误,是一件很蛋疼的事情,会增加队友们Debug的难度
// public void fly() {
// if (null == flyBehavior) {
// return;
// }
// flyBehavior.fly();
// }
public void fly() {
flyBehavior.fly();
}
public void quack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("所有的鸭子默认都会游泳");
}
// 加入set方法后就可以随时调用这两个方法来改变鸭子的行为(策略模式的精髓,算法之间的相互替换)
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
Fly接口的实现类
1,不会飞行:
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("我不会飞行,但是我有梦想");
}
}
2,用翅膀飞行
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("我有翅膀,可以飞行");
}
}
鸣叫行为实现类
1, 呱呱叫
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("呱呱呱....");
}
}
2, 沉默….
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("........");
}
}
具体的鸭子类(模型鸭)
public class ModelDuck extends Duck {
//这里的构造函数有两种形式
//第一种带有飞行行为和鸣叫行为的有参构造函数,这种形式就是完全将行为委托给客户端,
//自由度更高,但是客户端就必须要自己了解所有行为的所有算法,或者他需要用到的所有算法
// public ModelDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
// super(flyBehavior, quackBehavior);
// }
//第二种,设置默认的行为,客户端就不管飞行和鸣叫具体是怎么实现的,只要是模型鸭
//那么new ModelDuck().fly(),即可.
//在超类中有setFlyBehavior,setQuakBehavior,同样允许客户端自己选择
public ModelDuck() {
super(new FlyNoWay(), new MuteQuack());
}
@Override
public void display() {
System.out.println("我是一只模型鸭");
}
}
测试代码来说明什么是策略模式:
public class Client {
public static void main(String[] args) {
Duck duck = new ModelDuck();
duck.display();
System.out.println("默认飞行行为:");
duck.fly();
System.out.println("默认鸣叫行为:");
duck.quack();
//设置新的行为
duck.setFlyBehavior(new FlyWithWings());
duck.setQuackBehavior(new Quack());
System.out.println("新的飞行行为:");
duck.fly();
System.out.println("新的鸣叫行为:");
duck.quack();
//或者这样
duck.setFlyBehavior(new FlyBehavior() {
@Override
public void fly() {
System.out.println("我是火箭飞行模式");
}
});
duck.setQuackBehavior(new QuackBehavior() {
@Override
public void quack() {
System.out.println("学狗叫,汪汪汪");
}
});
System.out.println("自定义的飞行行为:");
duck.fly();
System.out.println("自定义的鸣叫行为:");
duck.quack();
}
}
现在每只鸭子的叫声和飞行都可以自己选择灵活配置,不仅可以切换鸭子,还可以切换鸭子的行为,这在原来的设计上是办不到的,现在加一种新的鸭子变得很简单了,而且不会对原来的设计造成影响。
第一次写东西,写的比较乱,表达的东西也不一定准确,虽然标题是策略模式,但是bb了很多跟策略模式无关的东西
要是想更清楚的了解策略模式可以去参考这位大神:http://blog.csdn.net/hguisu/article/details/7558249/
headfirst设计模式(1)—策略模式的更多相关文章
- HeadFirst设计模式之策略模式
什么是策略模式:它定义了一系列算法,可以根据不同的实现调用不同的算法 大多数的设计模式都是为了解决系统中变化部分的问题 一.OO基础 抽象.封装.多态.继承 二.OO原则 1.封装变化,如把FlyBe ...
- 设计模式:策略模式(Strategy)
定 义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...
- PHP设计模式之策略模式
前提: 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查 找.排序等,一种常用的方法是硬编码(Hard Cod ...
- JavaScript设计模式之策略模式(学习笔记)
在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...
- 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)
原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...
- JavaScript设计模式之策略模式
所谓"条条道路通罗马",在现实中,为达到某种目的往往不是只有一种方法.比如挣钱养家:可以做点小生意,可以打分工,甚至还可以是偷.抢.赌等等各种手段.在程序语言设计中,也会遇到这种类 ...
- 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查
原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...
- [design-patterns]设计模式之一策略模式
设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不 ...
- 设计模式入门,策略模式,c++代码实现
// test01.cpp : Defines the entry point for the console application.////第一章,设计模式入门,策略模式#include &quo ...
- 设计模式之策略模式和状态模式(strategy pattern & state pattern)
本文来讲解一下两个结构比较相似的行为设计模式:策略模式和状态模式.两者单独的理解和学习都是比较直观简单的,但是实际使用的时候却并不好实践,算是易学难用的设计模式吧.这也是把两者放在一起介绍的原因,经过 ...
随机推荐
- SQL Server 2012的附件失败,与硬链接的问题
1.我在系统上做了硬链接,也就是把C盘的某个目录,映射为D盘. 2.把数据库文件mdf和ldf放入D盘.结果,SQL Server的企业管理器,无法从D盘里加载mdf或ldf文件,并且在目录下无法显示 ...
- 开源的.Net ORM微型框架SuperHelper
SuperHelper——灵活通用的.开源的.Net ORM微型框架 SuperHelper是博主利用业余时间编写的一个ORM微型框架,除了可以提高开发效率,与其它ORM框架相比,博主更加喜欢Supe ...
- MySQL之GROUP BY用法误解
1.说明 “Group By”从字面意义上理解就是根据“By”指定的规则对数据进行分组,所谓的分组就是将一个“数据集”划分成若干个“小区域”,然后针对若干个“小区域”进行数据处理.(只是简单说明这个 ...
- 织梦不仅是链接到其他调用next
//打开系统文件织梦 /include/arc.archives.class.php 找到GetPreNext函数 function GetPreNext($gtype='') { $rs = ''; ...
- PL/SQL Developer 连接Oracle数据库详细配置方法
PL/SQL Developer 连接Oracle数据库详细配置方法 近段时间很多网友提出监听配置相关问题,客户终端(Client)无法连接服务器端(Server).本文现对监听配置作一简单介绍,给出 ...
- Visual Studio 2015 & C#6.0 试用报告,持续更新。
昨天早上看到了.net开源的消息,我是非常兴奋的,毕竟局限于Windows的.NET经常被人唾弃.VB暂且不提,C#常年被人指责跨平台性不佳,我也是无能为力.即使有Mono等第三方跨平台工程,.NET ...
- 六白话经典算法系列 高速分拣 高速GET
高速分拣,因为相同的排序效率O(N*logN)几个订购流程更高效,因此,经常使用,再加上高速分拣思想----分而治之的方法也是非常有用的,如此多的软件公司书面采访.它包含了腾讯,微软等知名IT企业宁 ...
- PHP中实现在数据库中的增、删、查、改
其实要想在PHP中访问并获取到数据库中的数据其实并不难,下面我以例子为大家介绍: 首先,打开PHP软件和WampServer服务,确保在WampServer中的phpMyAdmin中有你要使用的数据表 ...
- 在Idea中调试ant应用
Ant调试 Ant调试 ant 是一种非常方便的打包,部署的工具,通过ant,可以一键构建整个项目,虽然MVN也支持这种功能,但是MVN混杂了package管理的功能,并且不是很自由,学习成本比较高. ...
- SpringMVC表单标签
SpringMVC学习系列(11) 之 表单标签 本篇我们来学习Spring MVC表单标签的使用,借助于Spring MVC提供的表单标签可以让我们在视图上展示WebModel中的数据更加轻松. ...