策略模式 订阅
策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。 展开全文
策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。
信息
外文名
Pattern:Strategy
环境角色
持有一个策略类的引用
抽象策略角色
由一个接口或者抽象类实现
中文名
策略模式
具体策略角色
包装了相关的算法和行为
策略模式简介
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。策略模式:
收起全文
精华内容
下载资源
问答
  • java策略模式典型案例

    万次阅读 多人点赞 2019-05-25 00:32:21
    以一个顾客价格计算策略为背景,写一个策略模式的demo 参考代码 :https://github.com/zhang-xiaoxiang/DesignPatterns23 没有用策略模式我们一般是下面的写法,直接写一个类,在类里面直接写策略算法(功能实现) //...

    以一个顾客价格计算策略为背景,写一个策略模式的demo参考代码 : https://github.com/zhang-xiaoxiang/DesignPatterns23

    日常碰到的业务概述

    登录类型,支付类型,供应商渠道,不同等级会员享受的优惠券价格不一样,等等业务判断,大量if else导致拓展(侧重新增)极其困难,维护(侧重修改)自然是改起来头痛(其实一个类型的增加[拓展一个类型]往往对应这个类型的增删改查CRUD[维护]),比如业务一开始一个简单的登录,往往做一个电话号码和验证码登录的方式或者账号密码登录方式,后来随着业务的增加或者提高用户体验,那么需要拓展(新增一种)三方登录,比如新增微信登录,支付宝登录,甚至抖音登录,一大堆,你要根据登录方式来处理,那就有点恼火吧,支付方式也是同样的问题,我们可以发现一个规律,凡是可以枚举的业务,往往都需要使用设计模式才能更好的解决,比如策略模式(往往搭配工厂模式使用更配哦),水来土掩,兵来将挡,这思想和高中数学中的分类讨论思想一模一样.遇事不要慌,因为还有dang中央.所以,我们就打个栗子,举个比方,更加形象一点,

    没有用策略模式

    我们一般是下面的写法,直接写一个类,在类里面直接写策略算法(功能实现)

    //package com.demo.strategy;
    
    /**
     * NoStrategy:没有策略的做法
     * 实现起来比较容易,符合一般开发人员的思路
     * 假如,类型特别多,算法比较复杂时,整个条件语句的代码就变得很长,难于维护。
     * 如果有新增类型,就需要频繁的修改此处的代码!
     * 不符合开闭原则!---对这个类的修改要关闭,就是这个类要是写好了就不要去改他了,对类的功能的拓展要开放,显然只有面向接口编程才满足,
     * 所以策略模式Strategy这个接口(文中涉及到的)就应运而生了晒哈哈哈
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class NoStrategy {
    	/**
    	 * 传入客服等级类型获取相应的价格
    	 * @param type   会员类型(等级)
    	 * @param price  响应的价格
    	 * @return
    	 */
    	public double getPrice(String type, double price) {
    
    		if ("普通客户小批量".equals(type)) {
    			System.out.println("[未采用设计模式] 不打折,原价");
    			return price;
    		} else if ("普通客户大批量".equals(type)) {
    			System.out.println("[未采用设计模式] 打九折");
    			return price * 0.9;
    		} else if ("老客户小批量".equals(type)) {
    			System.out.println("[未采用设计模式] 打八折");
    			return price * 0.8;
    		} else if ("老客户大批量".equals(type)) {
    			System.out.println("[未采用设计模式] 打七折");
    			return price * 0.7;
    
    
    		//拓展一种策略
    		}else if("老客户特大批量".equals(type)){
    			System.out.println("[未采用设计模式] 打六折");
    			return price*0.6;
    		}
    
    
    		//乱传的也是当普通客户小批量(就是不打折)
    		return price;
    	}
    
    }
    

    策略模式

    看到上面的类,貌似想到一句话是不是,面向接口编程,上来就应该先是一个接口,所以改成接口

    1:面向接口编程,策略模式也是一样,上来先来个策略接口压压惊(领导先开会,把任务指明,通过策略获取价格)

    package com.demo.strategy;
    
    /**
     * Strategy:策略接口
     * 这个是对类NoStrategy改成面向接口的方式实现策略,不要像NoStrategy一样,
     * 直接写死策略的实现,而是使用这个接口先定义策略,功能实现后面再说
     * @author zhangxiaoxiang
     * @date 2019/5/24
     */
    public interface Strategy {
    	/**
    	 * 通过策略获取价格
    	 * @param standardPrice
    	 * @return
    	 */
    	 double getPrice(double standardPrice);
    }
    

    2:面向接口,组合编程,少用继承(继承虽然可以复用代码,但是会造成耦合度增加,解决方式往往采用接口做类的属性),如下,这样所有实现Strategy 的各种策略实现类都"组合"到这个类里面了,是不是所谓的高内聚啊

    //package com.demo.strategy;
    /**
     * Context:策略模式上下文---策略接收器,专门接收策略实现的算法
     * 负责和具体的策略类交互
     * 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化。
     * 如果使用spring的依赖注入功能,还可以通过配置文件,动态的注入不同策略对象,动态的切换不同的算法.
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class Context {
    	/**
    	 * 当前采用的算法对象
    	 * 面向接口,组合编程,少用继承
    	 * 简言之复杂类型(类,接口等)做属性
    	 */
    	private Strategy strategy;
    
    	/**
    	 * 选择策略Strategy实现类
    	 * 有参构造器(不写无参构造器,那么new 策略实现保证必须传一种策略,这里set方法也不用设置,
    	 * 设置了也没用(要设置set方法那么还是把无参构造也写出来才会有用,所以set伴随无参构造的感觉)
    	 * 这样同时也知道了为什么有参构造器设置了为什么无参构造器就失效了,JDK这样设计是有一定道理的,哈哈)
    	 * ---总之set注入也行,而且也推荐,也是一种组合/聚合的形式,只是这个例子采用构造器而已
    	 * @param strategy
    	 */
    	public Context(Strategy strategy) {
    		this.strategy = strategy;
    	}
    
    	public double getReultPrice(double price){
    		return this.strategy.getPrice(price);
    	}
    	//我的例子没有使用set方式注入而已,也可以使用它哈
    	// public void setStrategy(Strategy strategy) {
    	// 	this.strategy = strategy;
    	// }
    }
    
    

    3:既然是策略模式接口Strategy都明确了要做的事情是根据会员情况,返回价格,但是没有真正的实现,那么总有类来实现赛

    策略实现类1    VIP0Strategy 

    //package com.demo.strategy;
    /**
     * VIP0Strategy:普通会员策略
     *
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class VIP0Strategy implements Strategy {
    	/**
    	 * 输入一个价格,经过VIP0Strategy策略计算价格
    	 * @param standardPrice
    	 * @return
    	 */
    	@Override
    	public double getPrice(double standardPrice) {
    		System.out.println("[策略模式]普通会员 原价:"+standardPrice);
    		return standardPrice;
    	}
    
    }
    
    

     策略实现类2    VIP1Strategy 

    package com.demo.strategy;
    
    /**
     * VIP1Strategy: 一级会员策略
     *
     * @author zhangxiaoxiang
     * @date 2019/5/24
     */
    public class VIP1Strategy implements Strategy {
        /**
         * 输入一个价格,经过VIP1Strategy策略计算价格
         * @param standardPrice
         * @return
         */
        @Override
        public double getPrice(double standardPrice) {
            System.out.println("[策略模式]一级会员 打九折:"+standardPrice * 0.9);
            return standardPrice * 0.9;
        }
    
    }
    

    策略实现类3    VIP1Strategy 

    package com.demo.strategy;
    /**
     * VIP2Strategy:二级会员策略
     * @author zhangxiaoxiang
     * @date 2019/5/24
     */
    public class VIP2Strategy implements Strategy {
    	/**
    	 * 输入一个价格,经过VIP2Strategy策略计算价格
    	 * @param standardPrice
    	 * @return
    	 */
    	@Override
    	public double getPrice(double standardPrice) {
    		System.out.println("[策略模式]二级会员八折:"+standardPrice*0.8);
    		return standardPrice*0.8;
    	}
    
    }
    
    

     类似的策略实现类N ........随意拓展策略就是了

     客户端(使用者)使用----->后面加粗这段话可以忽略,也可以思考 客户端可以理解为前端传到后端的controller里面来了,我们设想,加入前端传入的需求(比如"普通客户大批量", 1000),那么仍然需要if else判断,因为这里需要选择策略,还是逃不掉选择困难症,如果已知前端的参数,那么这个策略也就知道了,就逃开if else的魔爪了,这也是策略模式的弊端(不过把所有策略通过IOC注入到一个map,然后使用get(入参),这样也是一种解决方式,不过不太优雅,反射+枚举估计是个不错的选择,老衲没有尝试的,有空再试),需要知道策略才行,不知道策略是谁,就不知道new 那个VIPNStrategy(),你仔细想一想是不是这个逻辑.至于new 策略可以使用spring 的IOC容器去做

    //package com.demo.strategy;
    
    /**
     * Client:策略模式客户端---Client 的main方法 可以想象成我们在使用别人写好的框架,我们有新的需求,对框架开发者来说就是需要对已有的
     * 代码进行维护升级,比如此时我们修改NoStrategy类,那么修改完后新版本的框架NoStrategy类很有能是对已经在使用的客户机制上不兼容的,如果
     * 用户升级为新版框架,遇到使用NoStrategy类的会报错,各种不兼容就不符合开发者维护的版本的规范,所以修改已有的类是极其不科学的
     *
     *
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class Client {
        public static void main(String[] args) {
            //System.out.println("------------------假如这里是service实现类,那么不知道使用哪种策略的就还是要根据参数new(或者spring IOC去取),感觉还是没有逃脱if else(所以结合工厂模式最佳啦,这个大伙可以思考一下,有空再更新这个博客)-----------------------");
            System.out.println("未使用模式-----------------------------------------");
            NoStrategy noStrategy=new NoStrategy();
            double price = noStrategy.getPrice("普通客户大批量", 1000);
            System.out.println(price);
            System.out.println("\n测试策略------------------------------------------");
            Context context0 = new Context(new VIP1Strategy());
            double resultPrice = context0.getReultPrice(1000);
            System.out.println(resultPrice);
    
    
            System.out.println("\n---怎么体现策略模式呢?比如现在需求是增加一种会员机制,  '老客户特大批量'  ,\n那么显然打折力度更大," +
                    "我们设置为6折,分别在未使用策略模式和使用了策略模式的基础上拓展,看那个更加易于拓展,方便维护---\n");
    
            //首先这这里,作为客户端只能够传入 "老客户特大批量" 和价格1000元,但是计算代码再服务器NoStrategy类里面,如果不去修改服务器NoStrategy
            //	那么这里是无法实现的,策略模式也是一样的,那么回到服务器端思考,不用设计模式就要修改NoStrategy里面的if else之类的代码,使用策略模式
            //    就要增加新的策略实现,其实差不太多
    
            //新增策略后未使用模式(会修该策略核心类)
            NoStrategy noStrategy1=new NoStrategy();
            double price1 = noStrategy1.getPrice("老客户特大批量", 1000);
            System.out.println(price1);
    
    
    
            //新增策略后使用模式(不会修改策略接口,只是添加一个实现)
            Context context2=new Context(new VPI4Strategy()) ;
            double price2 = context2.getReultPrice(1000);
            System.out.println(price2);
    
    
            System.out.println("\n结论:修改服务器端已经写好了的类是极其不好的维护形式,因为这个类NoStrategy" +
                    "\n可能在别的类中作为依赖或者叫做别的类引用了该类,在不明确的情况下,可能牵一发动全身,是不好的维护方式,使用了策略模式," +
                    "\n我们对只是添加了一个策略接口的实现,低侵入式,不会对已有代码造成影响,低耦合");
    
    
        }
    }
    

    使用模式的成果

    输出结果

     

    小结和抛出一些观点:上面基本已经完成策略模式的应用了,当然缺点就是调用端需要对策略有哪些熟悉,不知道的话就不能传入策略,所以这是最大的缺点.其实java的JDK8的函数式编程和Lambda表达式(简化匿名类等写法)可以让策略模式更加优雅,其实就是相当于JDK8新特性是把23中设计模式更加抽象的方式用在新语法上了,符合时代潮流,拓展java的函数式编程领域,可以大概参考哈新特性  https://zhangxiaoxiang.blog.csdn.net/article/details/100638661

    展开全文
  • 策略模式

    2017-04-12 15:09:06
    策略模式

    记得刚毕业工作时,粗略翻看过关于设计模式的书,
    之后在阅读android源码时,又断断续续地接触了些设计模式。

    一直想找个机会认真总结一下这方面的内容,
    拖延了好久,重于决定动手了。

    在这篇博客中,我们的主角是策略模式。


    我们先来看看一个具体的应用场景。

    假设我们现在负责设计一个游戏中的各种角色,
    例如骑士、战士、盗贼等。

    显然,骑士、战士、盗贼作为角色,肯定有共同的部分,
    因此我们首先会想到下面的设计方式:
    SouthEast

    Knight、Soldier和Thief均继承父类People,然后覆盖其中的fight和move函数,
    以实现各自的打斗和移动方式。

    在简单场景下,这么设计是可以满足需求的,
    但在复杂场景下,这么设计缺乏灵活性。

    例如,引入了武器系统后,Knight、Soldier和Thief的打斗方式可能与具体的武器有关;
    引入坐骑系统后,三个子类的移动方式就可能与具体的坐骑有关。

    同时,一个对象的武器和坐骑是可以改变的,
    因此要求它的打斗方式与移动方式能够在运行时动态调整。

    上图中提到的设计方式,将这些可能不断改变的部分与实际的对象绑定在一起,
    显然是不满足设计要求的。

    为了解决类似的问题,就需要使用策略模式了。


    我们将设计方式变更为下图:
    SouthEast

    如上图所示,我们在父类中增加成员变量fightBehaviour和moveBehaviour。
    这两个成员为接口类型,可以保存实际运行的对象。

    同时,父类提供了setFightBehaviour和setMoveBehaviour函数,
    用于调整fightBehaviour和moveBehaviour实际持有的对象,
    以提供运行时动态调整的能力。

    fight和move函数,将分别调用fightBehaviour和moveBehaviour的fight和move接口,
    具体的功能由对应的子类来实现。

    此时,People继承体系中变化的部分就被抽离出来了。

    其中,所有实现FightBehaviour接口的类,构成了战斗算法族;
    所有实现MoveBehaviour接口的类,构成了移动算法族。

    Knight、Soldier和Thief可以动态地从战斗算法族中选择战斗方式,
    从移动算法族中选择移动方式。

    有了这个例子后,我们就可以来看看策略模式的定义了:
    策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。


    现在,我们看一下Android中策略模式的例子。
    我们以属性动画的用法为例:

    ............
    //xml定义的属性动画
    Animator anim = AnimatorInflater.loadAnimator(this, R.animator.set_simple);
    //设置具体的插值器
    anim.setInterpolator(new DecelerateInterpolator(2f));

    从上面的代码可以看出,在创建动画对象后,我们可以动态的选择需要的插值器对象。

    Android中对应的设计结构类似于下图:

    SouthEast

    容易看出,不同的插值器实现TimeInterpolator接口,
    共同构成插值器算法族。
    Animator及其子类可以动态使用算法族中的任意对象。

    这就是很明显的策略模式。

    展开全文
  • 设计模式学习之策略模式

    万次阅读 多人点赞 2017-04-11 00:55:02
    如果在一个条件语句中又包含了多个条件语句就会使得代码变得臃肿,维护的成本也会加大,而策略模式就能较好的解决这个问题,本篇博客就带你详细了解策略模式策略模式的定义和使用场景定义:策略模式定义了一系列的...

    写代码时总会出很多的if…else,或者case。如果在一个条件语句中又包含了多个条件语句就会使得代码变得臃肿,维护的成本也会加大,而策略模式就能较好的解决这个问题,本篇博客就带你详细了解策略模式。
    策略模式的定义和使用场景

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

    分析下定义,策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。

    策略模式的使用场景:

    1.针对同一类型问题的多种处理方式,仅仅是具体行为有差别时;
    2.需要安全地封装多种同一类型的操作时;
    3.出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择具体子类时。

    UML类图

    这里写图片描述

    这个模式涉及到三个角色:

    环境(Context)角色:持有一个Strategy的引用。

    抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

    具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

    策略模式的典型代码如下:

    抽象策略类

    public interface Strategy {
        /**
         * 策略方法
         */
        public void strategyInterface();
    }
    

    具体策略类

    public class ConcreteStrategyA implements Strategy {
    
        @Override
        public void strategyInterface() {
            //相关的业务
        }
    
    }
    
    public class ConcreteStrategyB implements Strategy {
    
        @Override
        public void strategyInterface() {
            //相关的业务
        }
    
    }
    

    环境角色类

    public class Context {
        //持有一个具体策略的对象
        private Strategy strategy;
        /**
         * 构造函数,传入一个具体策略对象
         * @param strategy    具体策略对象
         */
        public Context(Strategy strategy){
            this.strategy = strategy;
        }
        /**
         * 策略方法
         */
        public void contextInterface(){
            
            strategy.strategyInterface();
        }
        
    }
    

    策略模式例子

    假设鹅厂推出了3种会员,分别为会员,超级会员以及金牌会员,还有就是普通玩家,针对不同类别的玩家,购买《王者农药》皮肤有不同的打折方式,并且一个顾客每消费10000就增加一个级别,那么我们就可以使用策略模式,因为策略模式描述的就是算法的不同,这里我们举例就采用最简单的,以上四种玩家分别采用原价(普通玩家),九折,八折和七价的收钱方式。

    那么我们首先要有一个计算价格的策略接口

    public interface CalPrice {
        //根据原价返回一个最终的价格
        Double calPrice(Double orgnicPrice);
    }
    

    下面是4种玩家的计算方式的实现

    public class Orgnic implements CalPrice {
    
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice;
        }
    }
    
    public class Vip implements CalPrice {
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice * 0.9;
        }
    }
    
    
    public class SuperVip implements CalPrice {
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice * 0.8;
        }
    }
    
    
    public class GoldVip implements CalPrice {
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice * 0.7;
        }
    }
    
    

    我们看客户类,我们需要客户类帮我们完成玩家升级的功能。

    public class Player {
        private Double totalAmount = 0D;//客户在鹅厂消费的总额
        private Double amount = 0D;//客户单次消费金额
        private CalPrice calPrice = new Orgnic();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价
    
        //客户购买皮肤,就会增加它的总额
        public void buy(Double amount) {
            this.amount = amount;
            totalAmount += amount;
            if (totalAmount > 30000) {//30000则改为金牌会员计算方式
                calPrice = new GoldVip();
            } else if (totalAmount > 20000) {//类似
                calPrice = new SuperVip();
            } else if (totalAmount > 10000) {//类似
                calPrice = new Vip();
            }
        }
    
        //计算客户最终要付的钱
        public Double calLastAmount() {
            return calPrice.calPrice(amount);
        }
    }
    

    接下来是客户端调用,系统会帮我们自动调整收费策略。

    
    public class Client {
        public static void main(String[] args) {
            Player player = new Player();
            player.buy(5000D);
            System.out.println("玩家需要付钱:" + player.calLastAmount());
            player.buy(12000D);
            System.out.println("玩家需要付钱:" + player.calLastAmount());
            player.buy(12000D);
            System.out.println("玩家需要付钱:" + player.calLastAmount());
            player.buy(12000D);
            System.out.println("玩家需要付钱:" + player.calLastAmount());
        }
    }
    
    

    运行以后会发现,第一次是原价,第二次是九折,第三次是八折,最后一次则是七价。这样设计的好处是,客户不再依赖于具体的收费策略,依赖于抽象永远是正确的。

    在上面的基础上,我们可以使用简单工厂来稍微进行优化

    public class CalPriceFactory {
        private CalPriceFactory(){}
        //根据客户的总金额产生相应的策略
        public static CalPrice createCalPrice(Player customer){
            if (customer.getTotalAmount() > 30000) {//3000则改为金牌会员计算方式
                return new GoldVip();
            }else if (customer.getTotalAmount() > 20000) {//类似
                return new SuperVip();
            }else if (customer.getTotalAmount() > 10000) {//类似
                return new Vip();
            }else {
                return new Orgnic();
            }
        }
    }
    

    这样就将制定策略的功能从客户类分离了出来,我们的客户类可以变成这样。

    public class Player {
        private Double totalAmount = 0D;//客户在鹅厂消费的总额
        private Double amount = 0D;//客户单次消费金额
        private CalPrice calPrice = new Orgnic();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价
    
        //客户购买皮肤,就会增加它的总额
        public void buy(Double amount) {
            this.amount = amount;
            totalAmount += amount;
            /* 变化点,我们将策略的制定转移给了策略工厂,将这部分责任分离出去 */
            calPrice = CalPriceFactory.createCalPrice(this);
        }
    
        //计算客户最终要付的钱
        public Double calLastAmount() {
            return calPrice.calPrice(amount);
        }
    
        public Double getTotalAmount() {
            return totalAmount;
        }
    }
    

    虽然结合简单工厂模式,我们的策略模式灵活了一些,但不免发现在工厂中多了if-else判断,也就是如果增加一个会员类别,我又得增加一个else-if语句,这是简单工厂的缺点,对修改开放。

    那有什么方法,可以较好的解决这个问题呢?那就是使用注解, 所以我们需要给注解加入属性上限和下限,用来表示策略生效的区间,用来解决总金额判断的问题。

    1.首先我们做一个注解,这个注解是用来给策略添加的,当中可以设置它的上下限

    //这是有效价格区间注解,可以给策略添加有效区间的设置
    @Target(ElementType.TYPE)//表示只能给类添加该注解
    @Retention(RetentionPolicy.RUNTIME)//这个必须要将注解保留在运行时
    public @interface PriceRegion {
        int max() default Integer.MAX_VALUE;
        int min() default Integer.MIN_VALUE;
    }
    
    

    可以看到,我们只是使用这个注解来声明每一个策略的生效区间,于是对策略进行修改

    @PriceRegion(max = 10000)
    public class Orgnic implements CalPrice {
    
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice;
        }
    }
    
    
    @PriceRegion(max=20000)
    public class Vip implements CalPrice {
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice * 0.9;
        }
    }
    
    @PriceRegion(min=20000,max=30000)
    public class SuperVip implements CalPrice {
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice * 0.8;
        }
    }
    
    
    @PriceRegion(min=3000)
    public class GoldVip implements CalPrice {
        @Override
        public Double calPrice(Double orgnicPrice) {
            return orgnicPrice * 0.7;
        }
    }
    

    接下来就是在策略工厂中去处理注解

    public class CalPriceFactory {
        private static final String CAL_PRICE_PACKAGE = "com.example.stragedemo";//这里是一个常量,表示我们扫描策略的包
    
        private ClassLoader classLoader = getClass().getClassLoader();
    
        private List<Class<? extends CalPrice>> calPriceList;//策略列表
    
        //根据玩家的总金额产生相应的策略
        public CalPrice createCalPrice(Player player) {
            //在策略列表查找策略
            for (Class<? extends CalPrice> clazz : calPriceList) {
                PriceRegion validRegion = handleAnnotation(clazz);//获取该策略的注解
                //判断金额是否在注解的区间
                if (player.getTotalAmount() > validRegion.min() && player.getTotalAmount() < validRegion.max()) {
                    try {
                        //是的话我们返回一个当前策略的实例
                        return clazz.newInstance();
                    } catch (Exception e) {
                        throw new RuntimeException("策略获得失败");
                    }
                }
            }
            throw new RuntimeException("策略获得失败");
        }
    
        //处理注解,我们传入一个策略类,返回它的注解
        private PriceRegion handleAnnotation(Class<? extends CalPrice> clazz) {
            Annotation[] annotations = clazz.getDeclaredAnnotations();
            if (annotations == null || annotations.length == 0) {
                return null;
            }
            for (int i = 0; i < annotations.length; i++) {
                if (annotations[i] instanceof PriceRegion) {
                    return (PriceRegion) annotations[i];
                }
            }
            return null;
        }
    
        //单例
        private CalPriceFactory() {
            init();
        }
    
        //在工厂初始化时要初始化策略列表
        private void init() {
            calPriceList = new ArrayList<Class<? extends CalPrice>>();
            File[] resources = getResources();//获取到包下所有的class文件
            Class<CalPrice> calPriceClazz = null;
            try {
                calPriceClazz = (Class<CalPrice>) classLoader.loadClass(CalPrice.class.getName());//使用相同的加载器加载策略接口
            } catch (ClassNotFoundException e1) {
                throw new RuntimeException("未找到策略接口");
            }
            for (int i = 0; i < resources.length; i++) {
                try {
                    //载入包下的类
                    Class<?> clazz = classLoader.loadClass(CAL_PRICE_PACKAGE + "." + resources[i].getName().replace(".class", ""));
                    //判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表
                    if (CalPrice.class.isAssignableFrom(clazz) && clazz != calPriceClazz) {
                        calPriceList.add((Class<? extends CalPrice>) clazz);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    
        //获取扫描的包下面所有的class文件
        private File[] getResources() {
            try {
                File file = new File(classLoader.getResource(CAL_PRICE_PACKAGE.replace(".", "/")).toURI());
                return file.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        if (pathname.getName().endsWith(".class")) {//我们只扫描class文件
                            return true;
                        }
                        return false;
                    }
                });
            } catch (URISyntaxException e) {
                throw new RuntimeException("未找到策略资源");
            }
        }
    
        public static CalPriceFactory getInstance() {
            return CalPriceFactoryInstance.instance;
        }
    
        private static class CalPriceFactoryInstance {
    
            private static CalPriceFactory instance = new CalPriceFactory();
        }
    }
    
    

    虽然工厂里的逻辑增加了,但是解耦的效果达到了,现在我们随便加入一个策略,并设置好它的生效区间,策略工厂就可以帮我们自动找到适应的策略。

    Android源码中的策略模式

    Android的源码中,策略模式最典型的就是属性动画中的应用.
    我们使用属性动画的时候,可以通过set方法对插值器进行设置.可以看到内部维持了一个时间插值器的引用,并设置了getter和setter方法,默认情况下是先加速后减速的插值器,set方法如果传入的是null,则是线性插值器。而时间插值器TimeInterpolator是个接口,有一个接口继承了该接口,就是Interpolator这个接口,其作用是为了保持兼容

    public interface TimeInterpolator {
    
        /**
         * Maps a value representing the elapsed fraction of an animation to a value that represents
         * the interpolated fraction. This interpolated value is then multiplied by the change in
         * value of an animation to derive the animated value at the current elapsed animation time.
         *
         * @param input A value between 0 and 1.0 indicating our current point
         *        in the animation where 0 represents the start and 1.0 represents
         *        the end
         * @return The interpolation value. This value can be more than 1.0 for
         *         interpolators which overshoot their targets, or less than 0 for
         *         interpolators that undershoot their targets.
         */
        float getInterpolation(float input);
    }
    
    

    平时我们使用的时候,通过设置不同的插值器,实现不同的动画速率变换效果,比如线性变换,回弹,自由落体等等。这些都是插值器接口的具体实现,也就是具体的插值器策略。

    @HasNativeInterpolator
    public class AccelerateDecelerateInterpolator extends BaseInterpolator
            implements NativeInterpolatorFactory {
        public AccelerateDecelerateInterpolator() {
        }
    
        @SuppressWarnings({"UnusedDeclaration"})
        public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
        }
    
        public float getInterpolation(float input) {
            return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
        }
    
        /** @hide */
        @Override
        public long createNativeInterpolator() {
            return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
        }
    }
    
    

    内部使用的时候直接调用getInterpolation方法就可以返回对应的值了,也就是属性值改变的百分比。

    属性动画中另外一个应用策略模式的地方就是估值器,它的作用是根据当前属性改变的百分比来计算改变后的属性值。该属性和插值器是类似的,有几个默认的实现。其中TypeEvaluator是一个接口。

    public interface TypeEvaluator<T> {
    
        /**
         * This function returns the result of linearly interpolating the start and end values, with
         * <code>fraction</code> representing the proportion between the start and end values. The
         * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
         * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
         * and <code>t</code> is <code>fraction</code>.
         *
         * @param fraction   The fraction from the starting to the ending values
         * @param startValue The start value.
         * @param endValue   The end value.
         * @return A linear interpolation between the start and end values, given the
         *         <code>fraction</code> parameter.
         */
        public T evaluate(float fraction, T startValue, T endValue);
    
    }
    

    TypeEvaluator的实现

    public class ArgbEvaluator implements TypeEvaluator {
        private static final ArgbEvaluator sInstance = new ArgbEvaluator();
    
        /**
         * Returns an instance of <code>ArgbEvaluator</code> that may be used in
         * {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
         * be used in multiple <code>Animator</code>s because it holds no state.
         * @return An instance of <code>ArgbEvalutor</code>.
         *
         * @hide
         */
        public static ArgbEvaluator getInstance() {
            return sInstance;
        }
    
        /**
         * This function returns the calculated in-between value for a color
         * given integers that represent the start and end values in the four
         * bytes of the 32-bit int. Each channel is separately linearly interpolated
         * and the resulting calculated values are recombined into the return value.
         *
         * @param fraction The fraction from the starting to the ending values
         * @param startValue A 32-bit int value representing colors in the
         * separate bytes of the parameter
         * @param endValue A 32-bit int value representing colors in the
         * separate bytes of the parameter
         * @return A value that is calculated to be the linearly interpolated
         * result, derived by separating the start and end values into separate
         * color channels and interpolating each one separately, recombining the
         * resulting values in the same way.
         */
        public Object evaluate(float fraction, Object startValue, Object endValue) {
            int startInt = (Integer) startValue;
            int startA = (startInt >> 24) & 0xff;
            int startR = (startInt >> 16) & 0xff;
            int startG = (startInt >> 8) & 0xff;
            int startB = startInt & 0xff;
    
            int endInt = (Integer) endValue;
            int endA = (endInt >> 24) & 0xff;
            int endR = (endInt >> 16) & 0xff;
            int endG = (endInt >> 8) & 0xff;
            int endB = endInt & 0xff;
    
            return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                    (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                    (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                    (int)((startB + (int)(fraction * (endB - startB))));
        }
    }
    

    在Volley框架中,同样使用到了策略模式,其中定义了一个缓存接口

    /**
     * An interface for a cache keyed by a String with a byte array as data.
     */
    public interface Cache {
        /**
         * Retrieves an entry from the cache.
         * @param key Cache key
         * @return An {@link Entry} or null in the event of a cache miss
         */
        public Entry get(String key);
    
        /**
         * Adds or replaces an entry to the cache.
         * @param key Cache key
         * @param entry Data to store and metadata for cache coherency, TTL, etc.
         */
        public void put(String key, Entry entry);
    
        /**
         * Performs any potentially long-running actions needed to initialize the cache;
         * will be called from a worker thread.
         */
        public void initialize();
    
        /**
         * Invalidates an entry in the cache.
         * @param key Cache key
         * @param fullExpire True to fully expire the entry, false to soft expire
         */
        public void invalidate(String key, boolean fullExpire);
    
        /**
         * Removes an entry from the cache.
         * @param key Cache key
         */
        public void remove(String key);
    
        /**
         * Empties the cache.
         */
        public void clear();
    
        /**
         * Data and metadata for an entry returned by the cache.
         */
        public static class Entry {
            /** The data returned from cache. */
            public byte[] data;
    
            /** ETag for cache coherency. */
            public String etag;
    
            /** Date of this response as reported by the server. */
            public long serverDate;
    
            /** The last modified date for the requested object. */
            public long lastModified;
    
            /** TTL for this record. */
            public long ttl;
    
            /** Soft TTL for this record. */
            public long softTtl;
    
            /** Immutable response headers as received from server; must be non-null. */
            public Map<String, String> responseHeaders = Collections.emptyMap();
    
            /** True if the entry is expired. */
            public boolean isExpired() {
                return this.ttl < System.currentTimeMillis();
            }
    
            /** True if a refresh is needed from the original data source. */
            public boolean refreshNeeded() {
                return this.softTtl < System.currentTimeMillis();
            }
        }
    
    }
    

    它有两个实现类NoCache和DiskBasedCache,使用的时候设置对应的缓存策略。

    最后,推送一下自己的微信公众号,喜欢的同学可以关注。
    在这里插入图片描述

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,471
精华内容 18,988
关键字:

策略模式