首页 JAVA策略模式详解

JAVA策略模式详解

举报
开通vip

JAVA策略模式详解JAVA策略模式详解 上章我们着重讲解了观察者模式和事件驱动,那么本章来讨论一个个人认为在开发过程中出场率极高的设计模式,策 略模式。 策略模式在LZ第一次接触到的时候,LZ是这么理解的,就是如果我们想往一个方法当中插入随便一段代码的话,就 是策略模式。即如下形式。 publicclassMyClass{ publicvoidmyMethod(){ System.out.println("方法里的代码"); //LZ想在这插入一段代码,而且这个代码是可以改变的,想怎么变就怎么变 System.out.p...

JAVA策略模式详解
JAVA策略模式详解 上章我们着重讲解了观察者模式和事件驱动,那么本章来讨论一个个人认为在开发过程中出场率极高的 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 模式,策 略模式。 策略模式在LZ第一次接触到的时候,LZ是这么理解的,就是如果我们想往一个方法当中插入随便一段代码的话,就 是策略模式。即如下形式。 publicclassMyClass{ publicvoidmyMethod(){ System.out.println("方法里的代码"); //LZ想在这插入一段代码,而且这个代码是可以改变的,想怎么变就怎么变 System.out.println("方法里的代码"); } } 在JAVA中,接口可以满足LZ的这一过分要求,我们可以设计一个接口,并当做参数传进去,就能达到这个效果了。 我们来看,先定义一个接口。 publicinterfaceMyInterface{ //我想插入的代码 voidinsertCode(); } 我们只要实现了MyInterface这个接口,在insertCode方法中写入我们想要插进去的代码,再将这个类传递给 myMethod方法,就可以将我们随手写的代码插到这个方法当中。比如这样。 classInsertCode1implementsMyInterface{ publicvoidinsertCode(){ System.out.println("我想插进去的代码,第一种"); } } classInsertCode2implementsMyInterface{ publicvoidinsertCode(){ System.out.println("我想插进去的代码,第二种"); } } 这样我们在调用myMethod方法时就可以随意往里面插入代码了,比如。 //客户端调用 publicclassClient{ publicstaticvoidmain(String[]args){ MyClassmyClass=newMyClass(); myClass.myMethod(newInsertCode1()); System.out.println("--------------------"); myClass.myMethod(newInsertCode2()); } } 那么运行出来的结果就是我们成功的将两端代码插入到了myMethod方法中,以上所讲的算是JAVA中一种技术层面的实现,就是传入一个接口,封装代码。那么既然谈到设计模式,就要有设计模式的应用场景,有关策略模式,所产生的形式就和上述是一模一样的,只是我们适当的给予模式的应用场景,就会让它变的更有价值。 下面LZ给出策略模式的 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 定义,引自百度百科。 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 下定义,策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。 下面给出策略模式的类图,引自百度百科。 这个类图并不复杂,右边是策略接口以及它的实现类,左边会有一个上下文,这个上下文会拥有一个策略,而具体这个策略是哪一种,我们是可以随意替换的。 LZ下面使用JAVA代码诠释上面的类图,方便各位理解各个类之间的关系。 首先是策略接口以及它的实现类。 那么运行出来的结果就是我们成功的将两端代码插入到了myMethod方法中,以上所讲的算是JAVA中一种技术层面的实现,就是传入一个接口,封装代码。那么既然谈到设计模式,就要有设计模式的应用场景,有关策略模式,所产生的形式就和上述是一模一样的,只是我们适当的给予模式的应用场景,就会让它变的更有价值。 下面LZ给出策略模式的标准定义,引自百度百科。 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 分析下定义,策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。 下面给出策略模式的类图,引自百度百科。 这个类图并不复杂,右边是策略接口以及它的实现类,左边会有一个上下文,这个上下文会拥有一个策略,而具体这 个策略是哪一种,我们是可以随意替换的。 LZ下面使用JAVA代码诠释上面的类图,方便各位理解各个类之间的关系。 首先是策略接口以及它的实现类。 下面是我们的上下文,它会拥有一个策略接口。 1. packagenet; 2. 3. 4. publicclassClient{ 5. 6. publicstaticvoidmain(String[]args)throwsException{ 7. Contextcontext=newContext(); 8. context.setStrategy(newConcreteStrategyA()); 9. context.method(); 10. 11. context.setStrategy(newConcreteStrategyB()); 12. context.method(); 13. 14. context.setStrategy(newConcreteStrategyC()); 15. context.method(); 16. } 上面我们替换了两次策略,但是调用方式不变,下面我们看下运行结果。 上面的例子代码清晰但却理解起来很生硬,下面LZ举一个具有实际意义的例子。 就比如我们要做一个商店的收银系统,这个商店有普通顾客,会员,超级会员以及金牌会员的区别,针对各个顾客, 有不同的打折方式,并且一个顾客每在商店消费1000就增加一个级别,那么我们就可以使用策略模式,因为策略模式 描述的就是算法的不同,而且这个算法往往非常繁多,并且可能需要经常性的互相替换。 这里我们举例就采用最简单的,以上四种顾客分别采用原价,八折,七折和半价的收钱方式。 那么我们首先要有一个计算价格的策略接口,如下。 publicinterfaceCalPrice{ //根据原价返回一个最终的价格 DoublecalPrice(DoubleoriginalPrice); } 下面我们给出四个计算方式。 classCommonimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice; } } classVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.8; } } classSuperVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.7; } } classGoldVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.5; } } 以上四种计算方式非常清晰,分别是原价,八折,七折和半价。下面我们看客户类,我们需要客户类帮我们完成客户 升级的功能。 //客户类 publicclassCustomer{ privateDoubletotalAmount=0D;//客户在本商店消费的总额 privateDoubleamount=0D;//客户单次消费金额 privateCalPricecalPrice=newCommon();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价 //客户购买商品,就会增加它的总额 publicvoidbuy(Doubleamount){ this.amount=amount; totalAmount+=amount; if(totalAmount>3000){//3000则改为金牌会员计算方式 calPrice=newGoldVip(); }elseif(totalAmount>2000){//类似 calPrice=newSuperVip(); }elseif(totalAmount>1000){//类似 calPrice=newVip(); } } //计算客户最终要付的钱 publicDoublecalLastAmount(){ returncalPrice.calPrice(amount); } } 下面我们看客户端调用,系统会帮我们自动调整收费策略。 //客户端调用 publicclassClient{ publicstaticvoidmain(String[]args){ Customercustomer=newCustomer(); customer.buy(500D); System.out.println("客户需要付钱:"+customer.calLastAmount()); customer.buy(1200D); System.out.println("客户需要付钱:"+customer.calLastAmount()); customer.buy(1200D); System.out.println("客户需要付钱:"+customer.calLastAmount()); customer.buy(1200D); System.out.println("客户需要付钱:"+customer.calLastAmount()); } } 运行以后会发现,第一次是原价,第二次是八折,第三次是七折,最后一次则是半价。我们这样设计的好处是,客户 不再依赖于具体的收费策略,依赖于抽象永远是正确的。不过上述的客户类实在有点难看,尤其是buy方法,我们可 以使用简单工厂来稍微改进一下它。我们建立如下策略工厂。 //我们使用一个标准的简单工厂来改进一下策略模式 publicclassCalPriceFactory{ privateCalPriceFactory(){} //根据客户的总金额产生相应的策略 publicstaticCalPricecreateCalPrice(Customercustomer){ if(customer.getTotalAmount()>3000){//3000则改为金牌会员计算方式 returnnewGoldVip(); }elseif(customer.getTotalAmount()>2000){//类似 returnnewSuperVip(); }elseif(customer.getTotalAmount()>1000){//类似 returnnewVip(); }else{ returnnewCommon(); } } } 这样我们就将制定策略的功能从客户类分离了出来,我们的客户类可以变成这样。 //客户类 publicclassCustomer{ privateDoubletotalAmount=0D;//客户在本商店消费的总额 privateDoubleamount=0D;//客户单次消费金额 privateCalPricecalPrice=newCommon();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价 //客户购买商品,就会增加它的总额 publicvoidbuy(Doubleamount){ this.amount=amount; totalAmount+=amount; /*变化点,我们将策略的制定转移给了策略工厂,将这部分责任分离出去*/ calPrice=CalPriceFactory.createCalPrice(this); } //计算客户最终要付的钱 publicDoublecalLastAmount(){ returncalPrice.calPrice(amount); } publicDoublegetTotalAmount(){ returntotalAmount; } publicDoublegetAmount(){ returnamount; } } 现在比之前来讲,我们的策略模式更加灵活一点,但是相信看过LZ博文的都知道,LZ最不喜欢elseif,所以策略模式也是有缺点的,就是当策略改变时,我们需要使用elseif去判断到底使用哪一个策略,哪怕使用简单工厂,也避免不了这一点。比如我们又添加一类会员,那么你需要去添加elseif。再比如我们的会员现在打九折了,那么你需要添加一个九折的策略,这没问题,我们对扩展开放,但是你需要修改elseif的分支,将会员的策略从八折替换为九折,这是简单工厂的诟病,在之前已经提到过,对修改开放。 在简单工厂模式一章中,LZ就遗留了一堆的elseif,并在工厂方法一章提供了解决 方案 气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载 ,但是LZ没有写上来,这次LZ将整个解决方案搬上来,之前的简单工厂可以使用相同的方法改进。 LZ在工厂方法一章中已经指明可以使用注解来处理这一问题,但是LZ只给出了实现的思路,没有给出具体实现的方式,本次刚好将之前的实现方式补上,如果各位掌握了这里的实现方式,那么对于简单工厂模式那一章的问题也自然而然可以轻松解决。 不过简单工厂那一章的问题相对这里更简单一点,因为那里我们只需要做一个servlet名称与Class引用的映射关系,就可以消除掉elseif,使用现有的映射直接创造servlet实例,但是在这里就不行了,因为我们涉及到了客户总金额的判断,这不再是一个简单的名称与策略的对应关系。 所以我们需要给注解加入属性上限和下限,用来表示策略生效的区间,用来解决总金额判断的问题。 下面我们一步一步来,首先我们做一个注解,这个注解是用来给策略添加的,当中可以设置它的上下限,我们来看。 packagecom.calprice; importjava.lang.annotation.ElementType; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; //这是我们的有效区间注解,可以给策略添加有效区间的设置 @Target(ElementType.TYPE)//表示只能给类添加该注解 @Retention(RetentionPolicy.RUNTIME)//这个必须要将注解保留在运行时 public@interfaceTotalValidRegion{ //为了简单,我们让区间只支持整数 intmax()defaultInteger.MAX_VALUE; intmin()defaultInteger.MIN_VALUE; } 这个注解很简单,我们只是用它来记录每一个策略的生效区间,下面我们就可以在我们的各个策略类里去设置我们的生效区间了,我们将策略类全部改成如下形式。 packagecom.calprice; @TotalValidRegion(max=1000)//设置普通的在0-1000生效,以下类似,不再注释 classCommonimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice; } } @TotalValidRegion(min=1000,max=2000) classVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.8; } } @TotalValidRegion(min=2000,max=3000) classSuperVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.7; } } @TotalValidRegion(min=3000) classGoldVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.5; } } 好了,我们使用注解表示每一个策略生效的区间,下面我们要做最重要的工作,就是处理注解。我们在策略工厂处理注解。这个类会变的比较复杂一点,所以LZ直接加注释。我们来看策略工厂,即改善后的简单工厂。 packagecom.calprice1; importjava.io.File; importjava.io.FileFilter; importjava.lang.annotation.Annotation; importjava.net.URISyntaxException; importjava.util.ArrayList; importjava.util.List; //我们使用一个标准的简单工厂来改进一下策略模式 publicclassCalPriceFactory{ privatestaticfinalStringCAL_PRICE_PACKAGE="com.calprice";//这里是一个常量,表示我们扫描策略的包,这是LZ的包名 privateClassLoaderclassLoader=getClass().getClassLoader();//我们加载策略时的类加载器,我们任何类运行时信息必须来自该类加载器 privateList>calPriceList;//策略列表 //根据客户的总金额产生相应的策略 publicCalPricecreateCalPrice(Customercustomer){ //在策略列表查找策略 for(Classclazz:calPriceList){ TotalValidRegionvalidRegion=handleAnnotation(clazz);//获取该策略的注解 //判断金额是否在注解的区间 if(customer.getTotalAmount()>validRegion.min()&&customer.getTotalAmount()clazz){ Annotation[]annotations=clazz.getDeclaredAnnotations(); if(annotations==null||annotations.length==0){ returnnull; } for(inti=0;i>(); File[]resources=getResources();//获取到包下所有的class文件 ClasscalPriceClazz=null; try{ calPriceClazz=(Class)classLoader.loadClass(CalPrice.class.getName());//使用相同的加载器加载策略接口 }catch(ClassNotFoundExceptione1){ thrownewRuntimeException("未找到策略接口"); } for(inti=0;iclazz=classLoader.loadClass(CAL_PRICE_PACKAGE+"."+resources[i].getName().replace(".class","")); //判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表 if(CalPrice.class.isAssignableFrom(clazz)&&clazz!=calPriceClazz){ calPriceList.add((Class)clazz); } }catch(ClassNotFoundExceptione){ e.printStackTrace(); } } } //获取扫描的包下面所有的class文件 privateFile[]getResources(){ try{ Filefile=newFile(classLoader.getResource(CAL_PRICE_PACKAGE.replace(".","/")).toURI()); returnfile.listFiles(newFileFilter(){ publicbooleanaccept(Filepathname){ if(pathname.getName().endsWith(".class")){//我们只扫描class文件 returntrue; } returnfalse; } }); }catch(URISyntaxExceptione){ thrownewRuntimeException("未找到策略资源"); } } publicstaticCalPriceFactorygetInstance(){ returnCalPriceFactoryInstance.instance; } privatestaticclassCalPriceFactoryInstance{ privatestaticCalPriceFactoryinstance=newCalPriceFactory(); } } 上述便是我们改善后的简单工厂,虽然比刚开始的简单工厂复杂了很多,但是我们的收益是很明显的,现在我们随便加入一个策略,并设置好它的生效区间,策略工厂就可以帮我们自动找到适应的策略。LZ在上面已经加了详细的注释,相信可以帮助各位看懂了。在这里再特别 说明 关于失联党员情况说明岗位说明总经理岗位说明书会计岗位说明书行政主管岗位说明书 一点,我们的策略实现类最好放在一个包中,这样我们可以扫描特定的包,可以加快初始化速度。 有了这个基于注解的简单工厂,我们还需要稍微改变下客户类,因为客户类本来是调用的工厂的静态方法,现在我们将工厂做成了单例,所以应该改成如下形式。 calPrice=CalPriceFactory.getInstance().createCalPrice(this);好了,现在各位直接再调用客户端代码,会产生与之前一样的结果,说明我们的策略正确选择了。各位可以自己再试一下,比如到4000就变成四折,然后修改下客户端,测试一下看是否能起效。 现在看来,我们已经使用简单工厂,注解,反射等技术将策略模式优化的非常完美了,我们可以随意新增策略,并且不需要修改原有的任何代码。 但是,其实我们的设计还是有些不完美的,因为它无法支持策略的重叠,这是什么意思呢, 就是说我们同一时间只能采用一种策略,假设我们商店现在有这么一个需求,假设到端午节了,我们商店要采取满1000返200,满2000返400的方式,并且原有的打折还要继续,这就相当于将返现金的活动与打折重叠计算了。 比如我是个金牌会员,假设我买了2000的东西,那么计算方式应该是先减去400为1600,再打五折,为800。最后这个会员只需要付800(靠,这减价有点狠,不过我们只是举个例子,各位不要太在意数字)。 这就相当于将两个策略重叠使用了,我们现在的设计无法支持这种方式。那怎么办,你可能会想,可恶的老板就爱改需求。不过请永远记得,优秀的程序猿面对各种刁难的需求,都可以轻松解决,而不是抱怨需求变的太频繁或者太不合理,因为现实会告诉你,抱怨是没用的,而且我们很多时候可以使用一些编程的技巧去容纳这种变化。 刚才的需求,也是在提醒我们在设计一个系统时要考虑全面,我们虽然不应该考虑一些本不存在或者发生概率很小的需求,但像商店或者商场这种灵活的促销方式,却是我们刚开始就应该考虑到的。 现在我们的需求变了,即我们任意的策略都可以随意组合,并且我们要求工厂帮我们自动判断,并将策略叠加返回给我们。那么针对上面的设计我们还需要改善,如果要改善一个设计,我们就需要考虑现有的设计不能支持什么需求。我们考虑上述设计不能支持什么。LZ列出以下两条。 1,我们只能根据客户消费的总金额去处理,而不能根据客户当次消费的金额去处理。 2,我们的设计只能支持单一策略,不能支持策略叠加。 为了满足这两个要求,我们需要添加一个类型的注解,去针对单次消费产生计费策略,另外,我们需要让策略工厂能够产生叠加的策略接口,那么冲着这个目标,我们首先定义如下三个注解,采用嵌套注解。 packagecom.calprice; importjava.lang.annotation.ElementType; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; //我们定义一个嵌套注解 @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) public@interfaceValidRegion{ intmax()defaultInteger.MAX_VALUE; intmin()defaultInteger.MIN_VALUE; //既然可以任意组合,我们就需要给策略定义下顺序,就比如刚才说的2000那个例子,按先返后打折的顺序是800, 反过来就是600了。 //所以我们必须支持这一特性,默认0,为最优先 intorder()default0; } 我们定义上面这个嵌套注解是为了避免代码的重复,因为这三个属性我们在总额消费的策略注解和单次消费的策略注 解中都要包括。下面给出另外两个注解。packagecom.calprice; importjava.lang.annotation.ElementType; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; //这是我们的总额有效区间注解,可以给策略添加有效区间的设置 @Target(ElementType.TYPE)//表示只能给类添加该注解 @Retention(RetentionPolicy.RUNTIME)//这个必须要将注解保留在运行时 public@interfaceTotalValidRegion{ //我们引用有效区间注解 ValidRegionvalue()default@ValidRegion; } 上述这个总额注解与之前的注解基本一样,直接换成了嵌套注解。下面还有一个一次性消费的注解。如下。 packagecom.calprice; importjava.lang.annotation.ElementType; importjava.lang.annotation.Retention; importjava.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target; //这是我们针对单次消费的有效区间注解,可以给策略添加有效区间的设置 @Target(ElementType.TYPE)//表示只能给类添加该注解 @Retention(RetentionPolicy.RUNTIME)//这个必须要将注解保留在运行时 public@interfaceOnceValidRegion{ //我们引用有效区间注解 ValidRegionvalue()default@ValidRegion; } 以上三个注解我们就可以支持刚才的第一个要求了,我们可以针对一次消费进行策略判断,接下来我们需要修改策略 工厂,去支持单次消费判断,并且还要支持策略重叠。如下。 packagecom.calprice; importjava.io.File; importjava.io.FileFilter; importjava.lang.annotation.Annotation; importjava.net.URISyntaxException; importjava.util.ArrayList; importjava.util.List; importjava.util.SortedMap; importjava.util.TreeMap; //我们使用一个标准的简单工厂来改进一下策略模式 publicclassCalPriceFactory{ privatestaticfinalStringCAL_PRICE_PACKAGE="com.calprice";//这里是一个常量,表示我们扫描策略的包,这是LZ的包名 privateClassLoaderclassLoader=getClass().getClassLoader();//我们加载策略时的类加载器,我们任何类运行时信息必须来自该类加载器 privateList>calPriceList;//策略列表 //根据客户的总金额产生相应的策略 publicCalPricecreateCalPrice(Customercustomer){ //变化点:为了支持优先级排序,我们采用可排序的MAP支持,这个Map是为了储存我们当前策略的运行时类信息 SortedMap>clazzMap=newTreeMap>(); //在策略列表查找策略 for(Classclazz:calPriceList){ AnnotationvalidRegion=handleAnnotation(clazz);//获取该策略的注解 //变化点:根据注解类型进行不同的判断 if(validRegioninstanceofTotalValidRegion){ TotalValidRegiontotalValidRegion=(TotalValidRegion)validRegion; //判断总金额是否在注解的区间 if(customer.getTotalAmount()>totalValidRegion.value().min()&&customer.getTotalAmount()onceValidRegion.value().min()&&customer.getAmount()clazz){ Annotation[]annotations=clazz.getDeclaredAnnotations(); if(annotations==null||annotations.length==0){ returnnull; } for(inti=0;i>(); File[]resources=getResources();//获取到包下所有的class文件 ClasscalPriceClazz=null; try{ calPriceClazz=(Class)classLoader.loadClass(CalPrice.class.getName());//使用相同的加载器加载策略接口 }catch(ClassNotFoundExceptione1){ thrownewRuntimeException("未找到策略接口"); } for(inti=0;iclazz=classLoader.loadClass(CAL_PRICE_PACKAGE+"."+resources[i].getName().replace(".class","")); //判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表 if(CalPrice.class.isAssignableFrom(clazz)&&clazz!=calPriceClazz){ calPriceList.add((Class)clazz); } }catch(ClassNotFoundExceptione){ e.printStackTrace(); } } } //获取扫描的包下面所有的class文件 privateFile[]getResources(){ try{ Filefile=newFile(classLoader.getResource(CAL_PRICE_PACKAGE.replace(".","/")).toURI()); returnfile.listFiles(newFileFilter(){ publicbooleanaccept(Filepathname){ if(pathname.getName().endsWith(".class")){//我们只扫描class文件 returntrue; } returnfalse; } }); }catch(URISyntaxExceptione){ thrownewRuntimeException("未找到策略资源"); } } publicstaticCalPriceFactorygetInstance(){ returnCalPriceFactoryInstance.instance; } privatestaticclassCalPriceFactoryInstance{ privatestaticCalPriceFactoryinstance=newCalPriceFactory(); } } 上面我们改动的地方并不多,主要是添加了一个单次消费的判断,另外就是没有直接返回策略实例,而是将满足条件的策略类信息传递给代理,产生一个代理,从而满足我们第二个要求,即策略可以重叠,下面LZ给出代理类,相信如果各位看过LZ的代理模式,并完全理解了,那么看懂这里面的道理是非常简单的,为此,LZ不在多做解释。代理类如下。 packagecom.calprice; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importjava.lang.reflect.Proxy; importjava.util.SortedMap; publicclassCalPriceProxyimplementsInvocationHandler{ privateSortedMap>clazzMap; privateCalPriceProxy(SortedMap>clazzMap){ super(); this.clazzMap=clazzMap; } publicObjectinvoke(Objectproxy,Methodmethod,Object[]args) throwsThrowable{ Doubleresult=0D; if(method.getName().equals("calPrice")){ for(Classclazz:clazzMap.values()){ if(result==0){ result=(Double)method.invoke(clazz.newInstance(),args); }else{ result=(Double)method.invoke(clazz.newInstance(),result); } } returnresult; } returnnull; } publicstaticCalPricegetProxy(SortedMap>clazzMap){ return(CalPrice)Proxy.newProxyInstance(CalPriceProxy.class.getClassLoader(),newClass[]{CalPrice.class},newCalPricePr oxy(clazzMap)); } } 好了,这下我们可以支持策略重叠了,我给各位一个指定好的一系列策略,如下。 packagecom.calprice;缪买网 www.miumai.com //我们使用嵌套注解,并且制定我们打折的各个策略顺序是99,这算是很靠后的 //因为我们最后打折算出来钱是最多的,这个一算就很清楚,LZ不再解释数学问题 @TotalValidRegion(@ValidRegion(max=1000,order=99)) classCommonimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice; } } @TotalValidRegion(@ValidRegion(min=1000,max=2000,order=99)) classVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.8; } } @TotalValidRegion(@ValidRegion(min=2000,max=3000,order=99)) classSuperVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.7; } } @TotalValidRegion(@ValidRegion(min=3000,order=99)) classGoldVipimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice*0.5; } } @OnceValidRegion(@ValidRegion(min=1000,max=2000,order=40)) classOneTDTwoHimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice-200; } } @OnceValidRegion(@ValidRegion(min=2000,order=40)) classTwotDFourHimplementsCalPrice{ publicDoublecalPrice(DoubleoriginalPrice){ returnoriginalPrice-400; } } 当然你还可以多写几个buy方法,看下我们的策略是否在发挥作用。这下我们的策略模式就更加灵活了,不仅可以支持策略的随意增加,而且还可以重叠。相信这下老板再要搞什么促销活动,都可以轻松应付了吧。当然,没有最好的设计只有更适合的设计,只要可以满足大部分需求,容纳大部分变化就算是很好的设计了。实在容纳和满足不了,我们还可以重构,而且重构往往会比预先设计更加凑效。 本次讲解策略模式算是由浅及深的方式,刚开始给出的是LZ作为一个JAVA新人的时候对策略模式的理解,后面是一个代入了业务场景的例子,以及后面的逐渐使用简单工厂,注解,反射,代理等方式改善我们的策略工厂的过程,算是LZ和各位读者一起进行一个设计思想的锻炼吧。 策略模式本身并不太复杂,实现也比较简单,但是我们却花费了大量的篇幅去完善它,这是因为完善策略模式往往比使用更加复杂。 最后总结一下策略模式的使用场景,就是有一系列的可相互替换的算法的时候,我们就可以使用策略模式将这些算法做成接口的实现,并让我们依赖于算法的类依赖于抽象的算法接口,这样可以彻底消除类与具体算法之间的耦合。 比如我们现在客户类,它就知道有个CalPrice接口可以计算最终价格,其它的它什么都不知道了,这不正是我们之前总纲中提到的最小知道原则吗, 当然策略模式也有缺点,就是我们不停的在各个算法间切换,造成很多逻辑判断,不过我们本章已经给各位提供了思路,我们是可以使用一些其他的模式或者JAVA的技术去消除这种逻辑判断的,对吧, 最最后,感谢各位的收看。
本文档为【JAVA策略模式详解】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_083599
暂无简介~
格式:doc
大小:90KB
软件:Word
页数:0
分类:生活休闲
上传时间:2017-09-21
浏览量:9