精华内容
下载资源
问答
  • 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的魔爪了,这也是策略模式的弊端,需要知道策略才行,不知道策略是谁,就不知道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

    展开全文
  • 【Windows】Win10家庭版启用组策略gpedit.msc

    万次阅读 多人点赞 2018-04-28 19:58:37
    【前言】大家都认为,Windows 10家庭版中并不包含组策略,其实不然,它是有相关文件的,只是不让你使用而已。那么我们让系统允许你使用就好了。【操作步骤】1、首先你需要在桌面上新建一个txt文本文档。然后将以下...

    转载请注明出处,原文链接:https://blog.csdn.net/u013642500/article/details/80138799

    【前言】

    大家都认为,Windows 10家庭版中并不包含组策略,其实不然,它是有相关文件的,只是不让你使用而已。那么我们让系统允许你使用就好了。

    【操作步骤】

    1、首先你需要在桌面上新建一个txt文本文档。然后将以下代码复制到这个新建的txt文本文档中。

    @echo off
    pushd "%~dp0"
    dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >List.txt
    dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientTools-Package~3*.mum >>List.txt
    for /f %%i in ('findstr /i . List.txt 2^>nul') do dism /online /norestart /add-package:"C:\Windows\servicing\Packages\%%i"
    pause

    如图所示:

    2、保存,关闭,重命名这个新建文本文档,修改其后缀.txt变成.cmd。

    3、右键单击这个“新建文本文档.cmd”,选择以管理员身份运行即可。

    4、运行结果如下图所示:

    运行完毕,你的电脑就可以使用组策略gpedit.msc了。

    展开全文
  • 数据库Sharding的基本思想和切分策略

    万次阅读 多人点赞 2011-01-24 16:32:00
    本文着重介绍sharding的基本思想和理论上的切分策略,关于更加细致的实施策略和参考事例请参考我的另一篇博文:数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示一、基本思想 Sharding的基本思想就要把一个...

    博主历时三年倾注大量心血创作的《大数据平台架构与原型实现:数据中台建设实战》一书已由知名IT图书品牌电子工业出版社博文视点出版发行,真诚推荐给每一位读者!点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,扫码进入京东购书页面!

     

    本文着重介绍sharding的基本思想和理论上的切分策略,关于更加细致的实施策略和参考事例请参考我的另一篇博文:数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示 

     

    一、基本思想

          Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题。不太严格的讲,对于海量数据的数据库,如果是因为表多而数据多,这时候适合使用垂直切分,即把关系紧密(比如同一模块)的表切分出来放在一个server上。如果表并不多,但每张表的数据非常多,这时候适合水平切分,即把表的数据按某种规则(比如按ID散列)切分到多个数据库(server)上。当然,现实中更多是这两种情况混杂在一起,这时候需要根据实际情况做出选择,也可能会综合使用垂直与水平切分,从而将原有数据库切分成类似矩阵一样可以无限扩充的数据库(server)阵列。下面分别详细地介绍一下垂直切分和水平切分.

          垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非
    常低,相互影响很小,业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业
    务模块所使用的表分拆到不同的数据库中。根据不同的表来进行拆分,对应用程序的影响也
    更小,拆分规则也会比较简单清晰。(这也就是所谓的”share nothing”)。



          水平切分于垂直切分相比,相对来说稍微复杂一些。因为要将同一个表中的不同数据拆
    分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后
    期的数据维护也会更为复杂一些。



          让我们从普遍的情况来考虑数据的切分:一方面,一个库的所有表通常不可能由某一张表全部串联起来,这句话暗含的意思是,水平切分几乎都是针对一小搓一小搓(实际上就是垂直切分出来的块)关系紧密的表进行的,而不可能是针对所有表进行的。另一方面,一些负载非常高的系统,即使仅仅只是单个表都无法通过单台数据库主机来承担其负载,这意味着单单是垂直切分也不能完全解决问明。因此多数系统会将垂直切分和水平切分联合使用,先对系统做垂直切分,再针对每一小搓表的情况选择性地做水平切分。从而将整个数据库切分成一个分布式矩阵。

     

    二、切分策略

          如前面所提到的,切分是按先垂直切分再水平切分的步骤进行的。垂直切分的结果正好为水平切分做好了铺垫。垂直切分的思路就是分析表间的聚合关系,把关系紧密的表放在一起。多数情况下可能是同一个模块,或者是同一“聚集”。这里的“聚集”正是领域驱动设计里所说的聚集。在垂直切分出的表聚集内,找出“根元素”(这里的“根元素”就是领域驱动设计里的“聚合根”),按“根元素”进行水平切分,也就是从“根元素”开始,把所有和它直接与间接关联的数据放入一个shard里。这样出现跨shard关联的可能性就非常的小。应用程序就不必打断既有的表间关联。比如:对于社交网站,几乎所有数据最终都会关联到某个用户上,基于用户进行切分就是最好的选择。再比如论坛系统,用户和论坛两个模块应该在垂直切分时被分在了两个shard里,对于论坛模块来说,Forum显然是聚合根,因此按Forum进行水平切分,把Forum里所有的帖子和回帖都随Forum放在一个shard里是很自然的。

          对于共享数据数据,如果是只读的字典表,每个shard里维护一份应该是一个不错的选择,这样不必打断关联关系。如果是一般数据间的跨节点的关联,就必须打断。

     

          需要特别说明的是:当同时进行垂直和水平切分时,切分策略会发生一些微妙的变化。比如:在只考虑垂直切分的时候,被划分到一起的表之间可以保持任意的关联关系,因此你可以按“功能模块”划分表格,但是一旦引入水平切分之后,表间关联关系就会受到很大的制约,通常只能允许一个主表(以该表ID进行散列的表)和其多个次表之间保留关联关系,也就是说:当同时进行垂直和水平切分时,在垂直方向上的切分将不再以“功能模块”进行划分,而是需要更加细粒度的垂直切分,而这个粒度与领域驱动设计中的“聚合”概念不谋而合,甚至可以说是完全一致,每个shard的主表正是一个聚合中的聚合根!这样切分下来你会发现数据库分被切分地过于分散了(shard的数量会比较多,但是shard里的表却不多),为了避免管理过多的数据源,充分利用每一个数据库服务器的资源,可以考虑将业务上相近,并且具有相近数据增长速率(主表数据量在同一数量级上)的两个或多个shard放到同一个数据源里,每个shard依然是独立的,它们有各自的主表,并使用各自主表ID进行散列,不同的只是它们的散列取模(即节点数量)必需是一致的。(

    本文着重介绍sharding的基本思想和理论上的切分策略,关于更加细致的实施策略和参考事例请参考我的另一篇博文:数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示 


    1.事务问题:
    解决事务问题目前有两种可行的方案:分布式事务和通过应用程序与数据库共同控制实现事务下面对两套方案进行一个简单的对比。
    方案一:使用分布式事务
        优点:交由数据库管理,简单有效
        缺点:性能代价高,特别是shard越来越多时
    方案二:由应用程序和数据库共同控制
         原理:将一个跨多个数据库的分布式事务分拆成多个仅处
               于单个数据库上面的小事务,并通过应用程序来总控
               各个小事务。
         优点:性能上有优势
         缺点:需要应用程序在事务控制上做灵活设计。如果使用  
               了spring的事务管理,改动起来会面临一定的困难。
    2.跨节点Join的问题
          只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。

    3.跨节点的count,order by,group by以及聚合函数问题
          这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

     

    参考资料:

    《MySQL性能调优与架构设计》

     

    注:本文图片摘自《MySQL性能调优与架构设计》一 书

     

     

     

    相关阅读:

    数据库分库分表(sharding)系列(五) 一种支持自由规划无须数据迁移和修改路由代码的Sharding扩容方案

     

     

     

    数据库分库分表(sharding)系列(四) 多数据源的事务处理

    数据库分库分表(sharding)系列(三) 关于使用框架还是自主开发以及sharding实现层面的考量

     

    数据库分库分表(sharding)系列(二) 全局主键生成策略

     

    数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示

     

    关于垂直切分Vertical Sharding的粒度

    数据库Sharding的基本思想和切分策略

     

     

     

     

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

    万次阅读 多人点赞 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,使用的时候设置对应的缓存策略。

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

    展开全文
  • 本文着重介绍sharding切分策略,如果你对数据库sharding缺少基本的了解,请参考我另一篇从基础理论全面介绍sharding的文章:数据库Sharding的基本思想和切分策略 第一部分:实施策略图1.数据库分库分表(sharding)...
  • 策略路由 路由策略 双点双向引入

    千次阅读 多人点赞 2020-07-10 01:33:17
    策略路由 路由策略 双点双向引入 一、策略路由 (一)策略路由–Policy-Based Routing—PBR 1、什么是策略路由: 对特定数据报文不按照路由表内的条目执行转发,根据需要按照某种策略改变数据报文转发路径。 2、...
  • 我认为quant(策略quant)的核心是策略,底层基础是系统。策略可以个逻辑,简单到我就要买或者卖,也可以是个复杂的模型。而支撑的是平稳测试和执行人的想法或者模型的系统,包括backtesting,模拟盘,风控,择时...
  • 路由策略 & 策略路由

    万次阅读 2019-08-29 11:45:43
    策略路由PBR(Policy-Based Routing)是一种依据用户制定的策略进行路由选择的机制,分为本地策略路由、接口策略路由和智能策略路由SPR(Smart Policy Routing)。 策略路由具有如下优点: 可以根据用户实际需求...
  • 策略模式

    千次阅读 2019-09-25 20:23:35
    策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略...
  • 四种线程池拒绝策略

    万次阅读 多人点赞 2019-08-08 20:50:38
    当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略: ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutio...
  • 本地策略、域策略

    千次阅读 2018-04-19 23:35:14
    本地策略、域策略一、本地安全策略概述1、本地安全策略:本地安全策略影响本地计算机的安全设置2、打开方法:控制面板 → 管理工具”→ 本地安全策略 → 运行secpol.msc命令3、本地安全策略的分类本地安全策略...
  • 策略路由与路由策略

    千次阅读 2019-08-01 17:29:52
    PBR策略路由 通过流量策略来执行选路的手段,直接针对流量,在路由表已经出来的情况下,让某些通信的流量不按路由表中的选路执行。 路由策略 在控制层面,在正常的路由策略之上,根据某种规则,通过改变一些参数,...
  • Redis 深入理解内存回收策略设计思想(过期键删除策略和内存淘汰策略
  • 策略模式之策略枚举

    千次阅读 2018-08-18 22:31:58
    在使用策略模式时,我们要创建相应的策略,然后根据不同的场景使用不同的策略,这是可以的,但毕竟那是一堆的策略类,使用起来代码的可读性、扩展性也就一般,这种情况下我们可以使用策略模式的升级版---策略枚举来...
  • 同源策略

    千次阅读 2020-01-11 12:39:31
    了解熟悉浏览器同源策略, 并解决同源策略带来的问题, 实现业务需求, 如单点登录. 2.学习/操作 2.1 介绍 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源...
  • 路由策略策略路由

    千次阅读 2018-10-16 11:21:54
    路由策略 1.什么是路由策略? 路由策略(Routing Policy)的作用是当路由器在发布、接收和引入路由信息时,可根据实际组网需要实施一些策略,以便对路由信息进行过滤或改变路由信息的属性,如: 控制路由的发布:...
  • 常用设计模式-策略模式

    万次阅读 2020-11-20 09:51:54
    模式简介 它定义了算法家族,分别封装起来,让它们间可以相互替换,此模式让算法的变化,不会影响到使用算法的用户。 模式实现 环境类(Context)用一个...具体策略类(ConcreteStrategy)以Strategy...
  • 数据库分库分表(sharding)系列(二) 全局主键生成策略

    万次阅读 多人点赞 2012-07-03 09:40:05
    本文将主要介绍一些常见的全局主键生成策略,然后重点介绍flickr使用的一种非常优秀的全局主键生成方案。关于分库分表(sharding)的拆分策略和实施细则,请参考该系列的前一篇文章:数据库分库分表(sharding)系列(一)...
  • 在服务器中,点击 开始--管理工具--组策略服务组策略对象--default domain policy如下面找到密码策略 修改更改完策略之后:打开cmd 管理员下执行命令 gpupdate /force :立即更新用户及组策略,不用重启立即生效可以...
  • Redis-数据删除策略以及逐出(淘汰)策略

    千次阅读 多人点赞 2020-06-21 18:09:25
    文章目录Redis-删除策略以及逐出(淘汰)策略篇Redis简介删除策略以及逐出策略什么是过期数据?Redis提供的删除策略定时删除惰性删除|被动删除定期删除|主动删除删除策略比对逐出(淘汰)策略介绍逐出(淘汰)算法策略...
  • Dubbo 的负载均衡策略:轮询策略

    千次阅读 2018-08-13 22:27:10
    个人公众号原文: Dubbo 的负载均衡策略:轮询策略
  • Dubbo 的负载均衡策略:随机策略

    千次阅读 2018-08-10 14:25:45
    个人公众号原文: Dubbo 的负载均衡策略:随机策略
  • 测试策略

    万次阅读 2017-12-12 21:18:01
    我们在做软件开发中,经常会遇到测试策略的要求,这个时候我们 就要对软件的测试策略进行了解 。在这里就给大家举一些小的例子希望能帮到你们,好了话不多说直接上干货了。 1. 简述测试策略的定义:  测试...
  • 进化策略

    千次阅读 2018-01-23 19:13:57
    进化策略和遗传算法统称为进化算法,二者的思想很类似,但步骤和应用方向有所差别。 对遗传算法感兴趣的可以参考博客:遗传算法讲解 这篇文章主要讲解进化策略,从以下三个方面入手: 进化策略的思想 进化策略与...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 319,660
精华内容 127,864
关键字:

策略