什么是策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化(摘自百度百科)

关键字:算法封装,相互替换,独立变化

算法封装表示,每个算法只提供接口,屏蔽实现的细节。相互替换很好理解,就是有一个共同的父类,当然父类不一定就是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)—策略模式的更多相关文章

  1. HeadFirst设计模式之策略模式

    什么是策略模式:它定义了一系列算法,可以根据不同的实现调用不同的算法 大多数的设计模式都是为了解决系统中变化部分的问题 一.OO基础 抽象.封装.多态.继承 二.OO原则 1.封装变化,如把FlyBe ...

  2. 设计模式:策略模式(Strategy)

    定   义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...

  3. PHP设计模式之策略模式

    前提: 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查 找.排序等,一种常用的方法是硬编码(Hard Cod ...

  4. JavaScript设计模式之策略模式(学习笔记)

    在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...

  5. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  6. JavaScript设计模式之策略模式

    所谓"条条道路通罗马",在现实中,为达到某种目的往往不是只有一种方法.比如挣钱养家:可以做点小生意,可以打分工,甚至还可以是偷.抢.赌等等各种手段.在程序语言设计中,也会遇到这种类 ...

  7. 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查

    原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...

  8. [design-patterns]设计模式之一策略模式

    设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不 ...

  9. 设计模式入门,策略模式,c++代码实现

    // test01.cpp : Defines the entry point for the console application.////第一章,设计模式入门,策略模式#include &quo ...

  10. 设计模式之策略模式和状态模式(strategy pattern & state pattern)

    本文来讲解一下两个结构比较相似的行为设计模式:策略模式和状态模式.两者单独的理解和学习都是比较直观简单的,但是实际使用的时候却并不好实践,算是易学难用的设计模式吧.这也是把两者放在一起介绍的原因,经过 ...

随机推荐

  1. 网页头一定要加的代码段(加注版)一行代码解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10

      网页头部常见的一段代码 <!--[if lt IE 7 ]><html class="ie6"><![endif]--> <!--[i ...

  2. jsp连接MySQL数据库显示GIS地理数据乱码问题的解决(select AsText(the_geom))

    oh,fuck,经过我昨天下午到今天的努力,终于将这一问题成功解决了,哈哈哈 问题详细描述: 我通过jsp页面连接上MySQL数据库,取出存在表中的地理数据(类型是geometry,具体有POINT. ...

  3. WCF总结笔记

    ------------------------windowform承载服务步骤: (1)定义契约: using System; using System.Collections.Generic; u ...

  4. Git 和 Github的关系

    惭愧,这个问题到昨天才弄明白! Git 其实是一种版本控制的协议,和SVN/CVS类似,git协议定义了一个版本控制相关的各个操作,和SVN/CVS不同的是,git采用的是分布式的方法,并不需要服务器 ...

  5. C#多线程--仓库问题引发的故事

    假设有这么个场景,一个仓库,里面有N件货物,现有六个搬运工(用线程模拟),其中2个向仓库放东西,4个往外搬东西.假设1秒能向里放2件货物,同时可向外搬3件货物(线程休眠),现在需要往里放M件货物,一旦 ...

  6. MVC中用Jpaginate分页

    MVC中用Jpaginate分页 So easy!(兼容ie家族)   看过几款分页插件,觉得Jpaginate比较简约,样式也比较容易的定制,而且体验也比较好,支持鼠标滑动效果.先上效果图: 整个过 ...

  7. DSP TMS320C6000基础学习(4)—— cmd文件分析

    DSP中的CMD文件是链接命令文件(Linker Command File),以.cmd为后缀. 在分析cmd文件之前,必需先了解 (1)DSP具体芯片的内存映射(Memory Map) (2)知道点 ...

  8. 关于MEF

    MEF(Managed Extensibility Framework)是.NET Framework 4.0一个重要的库,Visual Studio 2010 Code Editor的扩展支持也是基 ...

  9. xhEditor实现插入代码功能

    如果大家经常使用CSDN或者其他技术博客,都会有插入程序代码或脚本功能 开源中国 CSDN 这里介绍xhEditor实现插入代码功能,对xhEditor进行插件扩展 一.首先定义插件样式 <st ...

  10. ASP.NET MVC3开发-数据库篇之CodeFisrt开发(一)

    本文讲述了在ASP.NET MVC3开发中M层使用Code Fisrt如何进行开发,由于作者对ASP.NET MVC3的学习不是很深,所以写的都是些基本的内容(写的如有不正确的地方请评论指正),适合初 ...