设计模式之---策略模式

1.定义

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

这种定义的语句总是有一种通病,喜欢把一个简单事物复杂化,这里举个例子来说明一下策略模式到底是用来干啥的。

比如我用代码来解一道数学题,然后由于本人是一个天才,牛逼到不行的那种,我可以用N种解法来求解。每种解法都是一种解决方案,但每次使用时,需要根据外在的约束条件来决定到底使用哪种算法来求解,原来的方法大概是来一个switch,搞很多case,来决定到底用哪个方案。这种方式一旦要修改就比较麻烦了,现在可以用策略模式来处理这种问题了。


看图说话

附上UML图一张:
图片来源与网络

策略模式

分析一下这个图,在contect中有三种算法A,B,C可以解决,于是将这三种算法独立出去成为单独的类,他们有一个共同的父类,而Context类不需要与具体的算法类接触。


还是看看代码具体的实现

光看这个图还是比较模糊的。

先看一下算法的父类:

//算法的抽象父类
public abstract class StrategyAbs {

    //每一个算法中都应该有的方法
    abstract boolean DoWork();
}

所有的算法的具体实现不同,但是他们最终实现的功能或效果应该是相同的,因此讲道理是可以提取出一个或多个方法的。

然后是算法的具体实现:

//算法
public class AStrategy extends StrategyAbs {
    private static final String TAG = "Strategy";

    @Override
    void DoWork() {
        Log.d(TAG, "DoWork: i am A,i am working now");
    }
}

这里假设有A,B,C三种算法,都是一样的,这里只列出A的代码。

StrategyUtil:

class StrategyUtil {
    private StrategyAbs strategyAbs;

    //调用者在这里传进来的就是调用者想要使用的A,还是B还是C
    //传进来的都是StrategyAbs的子类,所以用StrategyAbs可以统一接收
    public void setStrategyType(StrategyAbs strategyType) {
        strategyAbs = strategyType;
    }

    //调用父类的方法,而父类的方法是抽象的,所以会自动执行相应子类的方法
    public void work() {
        strategyAbs.DoWork();
    }
}

调用者只会和这个类接触,调用者因此不用关心具体算法类是怎么实现的。

最后看到底怎么去调用:

xml布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="st.zlei.com.strategypattern.MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/a_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="do work By A!"
         />

    <Button
        android:id="@+id/b_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="do work By B!"
        />
    <Button
        android:id="@+id/c_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="do work By C!"
        />

    <Button
        android:id="@+id/overAll_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="do work By C!"
        />
</LinearLayout>

xml布局很简单,就长这样:

xml布局

实现的功能是点击不同的Button,就采用不同的策略。

调用者:

       a_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //获取对象
                StrategyUtil strategyUtil = new StrategyUtil();
                //通过setStrategyType注入想要采用策略类型
                strategyUtil.setStrategyType(new AStrategy());
                //调用策略的方法
                strategyUtil.work();
            }
        });

        b_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                StrategyUtil strategyUtil = new StrategyUtil();
                strategyUtil.setStrategyType(new BStrategy());
                strategyUtil.work();
            }
        });

        c_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                StrategyUtil strategyUtil = new StrategyUtil();
                strategyUtil.setStrategyType(new CStrategy());
                strategyUtil.work();
            }
        });

最后看一下打印结果:

打印结果

可以看到点击A按钮,策略A就开始working,点击B按钮,策略B开始working,点击C按钮,策略C开始working。

还有一点就是这种写法还可以自定义一种策略:

        a_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //获取对象
                StrategyUtil strategyUtil = new StrategyUtil();
                //通过setStrategyType注入想要采用策略类型
                //当在这里不传子类策略的对象,而是直接new 一个父类的抽象类,就必须要重写DoWork方法,已达到自定义的功能
                strategyUtil.setStrategyType(new StrategyAbs() {
                    @Override
                    boolean DoWork() {
                        return false;
                    }
                });
                //调用策略的方法
                strategyUtil.work();
            }
        });

番外:

现在假设有一种需求,我希望优先使用A策略,其次使用B策略,当A,B策略都无法工作时,才采用C策略。

这时候xml代码多加一个button了,像这样:

    <Button
        android:id="@+id/overAll_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="do work First By A then B then C"
        />

修改一下A,B,C策略的方法:

public class AStrategy extends StrategyAbs {
    private static final String TAG = "Strategy";

    //当返回true的时候,表示A策略可以正常工作,false表示A策略无法工作
    @Override
    boolean DoWork() {
        Random random = new Random();
        boolean b = random.nextBoolean();
        if (b){
            Log.d(TAG, "DoWork: i am A,i am working now");
        }else {
            Log.d(TAG, "DoWork: i am A,i am busying");
        }
        return b;
    }
}

public class BStrategy extends StrategyAbs {
    private static final String TAG = "Strategy";
    //当返回true的时候,表示B策略可以正常工作,false表示B策略无法工作
    @Override
    boolean DoWork() {
        Random random = new Random();
        boolean b = random.nextBoolean();
        if (b){
            Log.d(TAG, "DoWork: i am B,i am working now");
        }else {
            Log.d(TAG, "DoWork: i am B,i am busying");
        }
        return b;
    }
}

public class CStrategy extends StrategyAbs {
    private static final String TAG = "Strategy";

    @Override
    boolean DoWork() {
        //c策略作为A,B,的备用策略,它必须要保证自己是可用的。
        Log.d(TAG, "DoWork: i am C,i am working now");

        return true;
    }
}

添加一个策略
使用这个策略就可以按照需求顺序使用A,B,C策略

class OverALlStrategy extends StrategyAbs {
    AStrategy aStrategy = new AStrategy();
    BStrategy bStrategy = new BStrategy();
    CStrategy cStrategy = new CStrategy();
    @Override
    boolean DoWork() {
        if (!aStrategy.DoWork()) {
            if (!bStrategy.DoWork()) {
                cStrategy.DoWork();
            }
        }
        return false;
    }
}

调用者:

        overAll_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                StrategyUtil strategyUtil = new StrategyUtil();
                strategyUtil.setStrategyType(new OverALlStrategy());
                strategyUtil.work();
            }
        });

再看一下打印结果:


A可以工作
A不可以工作,B可以工作
AB都不可以工作,C可以工作

满足了优先使用A策略,再使用B策略,最后使用C策略的要求。


总结

到这里策略模式就讲完了,这种模式的可扩展性,可维护性更好,核心内容就在于setStrategyType这个方法,传入的是具体的策略子类,但是用抽象父类的引用去接收,接收的始终不变,可是传入的可以改变成不同的策略。因此要求所有子类的功能实现的方法是统一的,以便被抽象到父类中去。举个例子比如A,B,C策略的功能实现的方法都叫叫DoWork,父类才可以有抽象的方法DoWork,然后根据setStrategyType传入的具体策略的类型,会自动调用相应子类的DoWork方法,如果A策略的功能方法叫methodA,B策略的功能方法叫methodB,C策略的功能方法叫methodC。那么父类是无法调用的,因为父类是无法知道该调用哪个的。

这文使用的代码可以在本人github上找到-->传送门

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,559评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,531评论 18 399
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,709评论 2 17
  • 中国首届新微商暨网红颁奖盛典-傲华荟,在最具微商代表的城市——广州,完美的落下帷幕,本次大会超人气圆满成功!傲华荟...
    中道文化阅读 610评论 0 0
  • 有人问,大学室友对自己的成长影响大吗?我说,影响简直不要太大。 大学四年,大部分人接触最多的还是室友。关上门男生一...
    太阳的森林阅读 552评论 0 3