策略模式

1、从项目"模拟鸭子游戏"实例讲解策略模式:假如一个有个鸭子,他会叫、会飞、肤色表现不一样,有不同颜色的鸭子,而且叫声不同、飞的方式也不同,要设计这样一个项目

2、从面向对象的角度设计这个项目

2.1 以封装、继承、多态的三个特点会抽象出鸭子超类,扩展超类:

public abstract class Duck{

    public abstract void display();

    public void swim(){

        System.out.println("I can swim");

    }

    public void quack(){

        System.out.println("gaga~~");

    }

    public void fly(){

        System.out.println("I can fly");

    }

}

2.2 然后不同定义不同颜色的鸭子继承分类Duck:

public class GreenHeadDuckextends Duck {

@Override

    public void display() {

System.out.println("**GreenHead**");

    }

}

public class RedHeadDuckextends Duck {

@Override

    public void display() {

System.out.println("**ReadHeadDuck");

    }

}

2.3 若此时新增需求,添加鸭子会飞的功能,当然按照面向对象的继承原则,我们可以在超类里添加一个fly()方法,

public abstract class Duck {

...;

public void Fly() {

System.out.println("~~im fly~~");

}

};

出现的问题:这个fly方法一加,让所有的子类都会飞了,这不科学。

继承的问题:对类的局部改动,尤其对超类的局部改动,会影响子类其他部分。影响会有溢出效应。

2.4 这时可以继续按照面向对象的继承以及多态原则进行改造,把超类的fly()方法定义成抽象类,子类重写这个fly方法

父类:

public abstract class Duck {

    ...;

    public abstract void Fly() ;

};

子类:

public class GreenHeadDuck extends Duck {

    ...;

    public void Fly() {

        System.out.println("~~no fly~~");

    }

}

2.5 但问题又来了,如果又新增一个需求,添加会游泳的验证,而且不同的鸭子游泳的方式不一样,这时按照面向对象的原则,又在超类上添加一个抽象方法,接着子类重写。若再添加一个新功能,还这样无限重复。超类每挖一个坑,每个子类都要填,增加工作量。不是好的设计方式。

那应该如何设计呢?

方法:分析项目中变化与不变的部分,提取变化部分,抽象成接口+实现

3、利用策略模式改造项目

    3.1提取项目中变化的部分,抽象成接口。

//飞行接口

 public interface FlyBehavior {

        void fly();

      }

//叫声接口

    public interface QuackBehavior {

        void quack();

    }

    3.2 超类中引用这两个接口的变量,在对应的方法上调用接口的方法,提供空的构造函数。子类在其构造函数上初始化对应行为的实现类,便具有其行为。

//父类抽象类

//父类抽象类

public abstract class Duck{

/**

    * 飞行行为接口

    */

    FlyBehaviorflyBehavior;

    /**

    * 叫声行为接口

    */

    QuackBehaviorquackBehavior;

    /**

    * 提供空的构造函数,以便子类调用,子类调用时,new出来的行为是啥子类就是啥行为

    */

    public Duck(){

    }

    public void fly(){

        flyBehavior.fly();

    }

    public void swim(){

        System.out.println("I can swim");

    }

    public void quack(){

        quackBehavior.quack();

    }

    public abstract void display();

    public void setFlyBehavior(FlyBehavior flyBehavior) {

        this.flyBehavior = flyBehavior;

    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {

        this.quackBehavior = quackBehavior;

    }

}

    子类实现类

public class GreenHeadDuckextends Duck {

    public GreenHeadDuck(){

        flyBehavior =new GoodFlyBehavior();

        quackBehavior =new GaGaQuackBehavior();

    }

    @Override

    public void display() {

        System.out.println("**GreenHeadDuck");

    }

}

若要添加新的飞行方式或者添加新的叫声方式,只需写个实现类实现FlyBehavior或QuackBehavior接口。若要添加新需求,添加会游泳的鸭子,只需新创建各SwimBehavior接口,提供实现类,并在超类上添加这个属性,各子类需要游泳行为的,在其构造函数上,给swimBehavior赋值响应的实现类即可。

4、使用策略模式相比于面向对象的好处:

    新增行为简单,行为类更好的复用,组合更方便。既有继承带来的复用好处,没有挖坑。

5、方法论:

当需要新的设计方式,应对项目的可扩展性,降低复杂度:

1)分析项目变化与不变的部分,提取变化部分,抽象成接口+实现

2)多用组合少用继承;用行为类组合,而不是行为的继承。更有弹性。

6、策略模式:

分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。

1)定义

策略模式是行为模式的一种,它对一系列的算法加以封装,为算又的算法定义一个抽象的算法接口,并通过继承该抽象算法接口对所有的算法加以封装和实现,具体算法选择交由客户端决定。

2)原则

分离变化部分,封装接口,基于接口编程各种功能。此模式让行为算法的变化独立于算法的使用者。

3)策略模式的角色和原则

    1、Strategy:策略抽象(算法抽象),本例的算法抽象是行为接口(FlyBehavior)以及叫声接口(QuackBehavior)

    2、ConcreteStrategy:各种策略算法的具体实现,本例的策略算法的具体实现是GoodFlyBehavior、BadFlyBehavior、GuGuQuackBehavior、GaGaQuackBehavior

    3、Context:策略的外部封装类,或者说是策略的容器类。根据不同的策略执行不同的行为。策略由外部环境决定的。本例中Context是GreenHeadDuck、RedHeadDuck

4)策略模式的优点

    1、策略模式提供了管理相关的算法族的办法,策略类的登级结构定义了一个算法或行为族。恰当地使用继承可以把公共的代码移到父类里面,从而避免重复的代码

    2、策略模式提供了可以替换继承关系的办法。继承可以处理多中算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为,但是,这样一来算法或行为的使用者和算法和行为本身混在了一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。

    3、使用策略模式可以避免多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。

5)策略模式的缺点

    1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道的所有的算法或行为的情况

    2、策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面。而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量

7、Spring中使用的策略模式

1)spring资源访问的方式


Resource接口是Spring资源访问策略的抽象,它本身并不提供任何资源访问实现,具体资源访问由该接口的实现类完成——每个实现类代表一种资源访问。

Spring 为 Resource 接口提供了如下实现类:

    UrlResource:访问网络资源的实现类。

    ClassPathResource:访问类加载路径里资源的实现类。

    FileSystemResource:访问文件系统里资源的实现类。

    ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:

    InputStreamResource:访问输入流资源的实现类。

    ByteArrayResource:访问字节数组资源的实现类。

        从上面的例子中可以看到,使用策略模式可以让客户端在不同的飞行行为和叫声行为中切换,但也有一个小小的不足,客户端代码需要和不同的策略类耦合。

下面我们回到spring框架里,看看Spring框架的Context如何智能地选择资源访问策略。

ResourceLoader接口和ResourceLoaderAware接口

spring提供了两个标志性接口:

    ResourceLoader:该接口的实现类的实例可以获得一个Resource实例。

    ResourceLoaderAware:该接口的实现类的实例将获得一个ResourceLoader实例。

在ResourceLoader接口里有如下方法:

        Resource getResource(String location):该接口仅包含这个方法,该方法用于返回一个 Resource 实例。ApplicationContext 的实现类都实现 ResourceLoader 接口,因此 ApplicationContext 可用于直接获取 Resource 实例。

此处spring框架的ApplicationContext不仅是Spring容器,而且它还是资源访问策略的"决策者",也就是策略模式中Context对象,它将为客户端代码"智能"地选择策略实现。

当ApplicationContext 实例获取Resource实例时,系统将默认采用与ApplicationContext相同的资源访问策略。对于如下代码:

//通过ApplicationContext访问资源

Resource res = ctx.getResource("application.xml");

从上面代码中无法确定 Spring 将哪个实现类来访问指定资源,Spring 将采用和 ApplicationContext 相同的策略来访问资源。也就是说:如果 ApplicationContext 是 FileSystemXmlApplicationContext,res 就是 FileSystemResource 实例;如果 ApplicationContext 是 ClassPathXmlApplicationContext,res 就是 ClassPathResource 实例;如果 ApplicationContext 是 XmlWebApplicationContext,res 是 ServletContextResource 实例.

看如下示例程序,下面程序将使用 ApplicationContext 来访问资源:

public static void main(String[] args) {

    ApplicationContext context =new ClassPathXmlApplicationContext("beans.xml");

    Resource res = context.getResource("beans.xml");

    System.out.println(res.getFilename());

    System.out.println(res.getDescription());

}

结果:

beans.xml

class path resource [beans.xml]

8、阿里支付宝官方支付sdk中使用的策略模式

    1)策略模式中Context对象是AlipayClient

    2)策略模式中的Strategy对象是AlipayRequest、AlipayResponse

            这两个接口封装了所有的支付宝请求、和返回值。

        AlipayResponse代码

public abstract class AlipayResponseimplements Serializable {

private static final long serialVersionUID =5014379068811962022L;

    @ApiField("code")

private String code;

    @ApiField("msg")

private String msg;

    @ApiField("sub_code")

private String subCode;

    @ApiField("sub_msg")

private String subMsg;

    private String body;

    private Map params;

    public AlipayResponse() {

}

/** @deprecated */

    @Deprecated

public String getErrorCode() {

return this.getCode();

    }

/** @deprecated */

    @Deprecated

public void setErrorCode(String errorCode) {

this.setCode(errorCode);

    }

public String getCode() {

return this.code;

    }

public void setCode(String code) {

this.code = code;

    }

public String getMsg() {

return this.msg;

    }

public void setMsg(String msg) {

this.msg = msg;

    }

public String getSubCode() {

return this.subCode;

    }

public void setSubCode(String subCode) {

this.subCode = subCode;

    }

public String getSubMsg() {

return this.subMsg;

    }

public void setSubMsg(String subMsg) {

this.subMsg = subMsg;

    }

public String getBody() {

return this.body;

    }

public void setBody(String body) {

this.body = body;

    }

public Map getParams() {

     return this.params;

    }

public void setParams(Map params) {

this.params = params;

    }

public boolean isSuccess() {

return StringUtils.isEmpty(this.subCode);

    }

}


AlipayRequest代码

public interface AlipayRequest {

String getApiMethodName();

    Map getTextParams();

    String getApiVersion();

    void setApiVersion(String var1);

    String getTerminalType();

    void setTerminalType(String var1);

    String getTerminalInfo();

    void setTerminalInfo(String var1);

    String getProdCode();

    void setProdCode(String var1);

    String getNotifyUrl();

    void setNotifyUrl(String var1);

    String getReturnUrl();

    void setReturnUrl(String var1);

    Class getResponseClass();

    boolean isNeedEncrypt();

    void setNeedEncrypt(boolean var1);

    AlipayObject getBizModel();

    void setBizModel(AlipayObject var1);

}


    3)ConcreteStrategy算法具体实现对象:AlipayTradePagePayRequest、AlipayTradePageResponse

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 本章目录如下: 一、阶段一 二、阶段二 三、策略模式说明 四、本章用到的OO设计原则 五、模式问答 六、本章要点 ...
    黑夜0411阅读 655评论 0 0
  • 设计模式 开题先说明一下,设计模式告诉我们如何组织类和对象以解决某种问题。让代码变得更加优雅是我们责无旁贷的任务 ...
    tanghuailong阅读 443评论 0 2
  • 一、需求引入 鸭子有不同的种类:绿头鸭、红头鸭、橡皮鸭...,它们都有相同点和不同点,相同点是它们都会游泳,不...
    Javar阅读 371评论 0 0
  • 这几天小车坏了,送进了维修厂! 然后我就没有小车开了,真的去哪也不方便啊! 到了维修的第三天,我开始怀念起我的小车...
    君临成长日记阅读 225评论 0 0
  • 仙城微雨后,秋气透窗纱。 枝上寻黄叶,风前数落花。 拈来云朵赋,拾得月轮斜。 莫负清凉意,闲添一盏茶。
    巫小皇阅读 281评论 0 3