博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
headfirst设计模式(1)—策略模式
阅读量:7030 次
发布时间:2019-06-28

本文共 5424 字,大约阅读时间需要 18 分钟。

什么是策略模式

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

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

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

转载于:https://www.cnblogs.com/skyseavae/p/6280136.html

你可能感兴趣的文章
我的友情链接
查看>>
mysql 1449 : The user specified as a definer ('xxx'@'%') does not exist
查看>>
Apache 2.2配置段和容器
查看>>
Azure Stack技术深入浅出系列6:Azure Stack一体机探究 — 揭开黑盒子的神秘面纱
查看>>
HTTP严格传输安全协议 (HSTS)
查看>>
实战虚拟化存储设计之一
查看>>
网络设计之二vLAN
查看>>
Exchange2013证书配置(超详细)
查看>>
【vSphere故障案例】案例七:数据中心虚拟化网络故障
查看>>
我的友情链接
查看>>
“你是怎么利用时间的?”之二
查看>>
我的友情链接
查看>>
CSS 层叠式样式表
查看>>
mysql5.6 开启慢连接日志
查看>>
数据库
查看>>
Apache Kylin 深入Cube和查询优化
查看>>
backbone.js 框架 II
查看>>
freemarker总结
查看>>
eclipse中安装velocity插件
查看>>
Sql 中常用日期转换Convert(Datetime)
查看>>