热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

策略模式–设计模式之行为模式

策略模式–设计模式之行为模式目录定义:类图:例子:类图:抽象的策略角色:IDiscount具体策

策略模式 – 设计模式之行为模式

目录

定义:

类图:

例子:

类图:

抽象的策略角色: IDiscount

具体策略角色1 OrdinaryStrategy

具体策略角色2 PlatinumStrategy

具体策略角色3 GoldStrategy

封装角色  DiscountStrategyContext

测试; DiscountTest

优化1:结合工厂方法

枚举类: UserTypeEnum

抽象的策略角色: IDiscount

具体策略角色1 OrdinaryStrategy

具体策略角色2 PlatinumStrategy

具体策略角色3 GoldStrategy

工厂方法类  DiscountStrategyFactory

测试; DiscountFactoryTest

优化2: 使用静态工厂,动态获取类

动态实例化类: SpringUtils

抽象的策略角色: IDiscount

具体策略角色1 OrdinaryStrategy

具体策略角色2 PlatinumStrategy

具体策略角色3 GoldStrategy

工厂方法类  DiscountStrategyFactory

封装角色; DiscountStrategyServiceContext

测试; DisountApplicationTests

总结:



 


定义:

 Define a family of algorithms, encapsulate each one, and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换)

定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式算法的变化,不会影响到使用算法的用户。

 


类图:

 

 

 


例子:

用常见的购物场景,普通的不打折,白金会员打8折,铂金会员打6折

 


类图:

 


抽象的策略角色: IDiscount

public interface IDiscount {double compute(double money);
}

 


具体策略角色1 OrdinaryStrategy

public class OrdinaryStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("普通会员不打折");System.out.println("积分是1%");return money;}}

 


具体策略角色2 PlatinumStrategy

public class PlatinumStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("白金会员 优惠50元,再打7折");System.out.println("积分是5%");return (money - 50) * 0.7;}}

 


具体策略角色3 GoldStrategy

public class GoldStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("黄金会员 优惠50元,再打6折");System.out.println("积分是10%");return (money - 50)*0.6;}}

 


封装角色  DiscountStrategyContext

public  class DiscountStrategyContext {private IDiscount discountStrategy;/*** 设置策略接口* @param discountHandleStrategy*/public void setDiscountHandleStrategy(IDiscount discountHandleStrategy) {this.discountStrategy = discountHandleStrategy;}public void computeMoney(double money) {if (discountStrategy != null) {double compute = discountStrategy.compute(money);System.out.println(" pay "+ compute);}}}

 


测试; DiscountTest

public class DiscountTest {public static void main(String[] args) {IDiscount discountStrategy = new OrdinaryStrategy();DiscountStrategyContext discountStrategyCOntext= new DiscountStrategyContext();discountStrategyContext.setDiscountHandleStrategy(discountStrategy);discountStrategyContext.computeMoney(100);}}

 

结果:

普通会员不打折积分是1%pay 100.0

思考: 在使用策略模式的时候,得先知道是哪个策略,还不是得if-else 进行判断,具体使用哪个策略。这个要怎么进行优化呢?只要知道一个名字就可以了,传递相关的数字或是名字进来,反馈一个结果,这才是想要的。

传递名字: 考虑用枚举,如例子中,三种会员的类型,相对固定。

对各个策略进行集中处理,考虑用工厂方法模式来实现策略类的声明。要知道策略的名称,接口那需要添加一个获取类型的方法。

 


优化1:结合工厂方法

 


枚举类: UserTypeEnum

public enum UserTypeEnum {COMMON_USER("COMMON"), PLATINUM_VIP("PLATINUM"),  GOLD_VIP("GOLD");String type;public String getType() {return type;}UserTypeEnum(String type) {this.type = type;}public static UserTypeEnum matchType(String type) {for (UserTypeEnum enumType : UserTypeEnum.values()) {if (enumType.type == type) {return enumType;}}return UserTypeEnum.COMMON_USER;}}

 


抽象的策略角色: IDiscount

public interface IDiscount {double compute(double money);// 用于匹配类型String getType();}

 


具体策略角色1 OrdinaryStrategy

public class OrdinaryStrategy implements IDiscount { @Overridepublic double compute(double money) {System.out.println("普通会员不打折");System.out.println("积分是1%");return money;}@Overridepublic String getType() {return UserTypeEnum.COMMON_USER.getType();}}

 


具体策略角色2 PlatinumStrategy

public class PlatinumStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("白金会员 优惠50元,再打7折");System.out.println("积分是5%");return (money - 50) * 0.7;}@Overridepublic String getType() {return UserTypeEnum.PLATINUM_VIP.getType();}}

 


具体策略角色3 GoldStrategy

public class GoldStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("黄金会员 优惠50元,再打6折");System.out.println("积分是10%");return (money - 50)*0.6;}@Overridepublic String getType() {return UserTypeEnum.GOLD_VIP.getType();}}

 

 


工厂方法类  DiscountStrategyFactory

public class DiscountStrategyFactory {private Map map;private DiscountStrategyFactory() {List strategies = new ArrayList<>();strategies.add(new OrdinaryStrategy());strategies.add(new GoldStrategy());strategies.add(new GoldStrategy());strategies.add(new PlatinumStrategy());// getType 主要在这边声明的时候用map = strategies.stream().collect(Collectors.toMap(IDiscount::getType, strategy -> strategy, (x, y) -> x));}public static class Holder {private static DiscountStrategyFactory instance = new DiscountStrategyFactory();}public static DiscountStrategyFactory getInstance() {return Holder.instance;}public IDiscount getIDiscountByType(String type) {return map.get(type);}}

 

或是用静态的方式,直接map,这样就不用声明getType() 和实现这个方法了

 DiscountStrategyFactoryStatic:

public class DiscountStrategyFactoryStatic {public static Map DICOUNT_TYPE = new HashMap<>();static {DICOUNT_TYPE.put("COMMON",new OrdinaryStrategy());DICOUNT_TYPE.put("PLATINUM",new GoldStrategy());DICOUNT_TYPE.put("GOLD",new PlatinumStrategy());}public static class Holder {private static DiscountStrategyFactoryStatic instance = new DiscountStrategyFactoryStatic();}public static DiscountStrategyFactoryStatic getInstance() {return Holder.instance;}public IDiscount getIDiscountByType(String type) {return DICOUNT_TYPE.get(type);}
}

测试; DiscountFactoryTest

public class DiscountFactoryTest {private static double getResult(long money, String type) {if (money <1000) {return money;}IDiscount strategy = DiscountStrategyFactory.getInstance().getIDiscountByType (type);if (strategy == null) {throw new IllegalArgumentException("please input right type");}return strategy.compute(money);}public static void main(String[] args) {String type = "GOLD";UserTypeEnum userTypeEnum = UserTypeEnum.matchType(type);System.out.println(getResult(3300, userTypeEnum.getType()));System.out.println(getResult(2300, "PLATINUM"));}}

 

结果:

黄金会员 优惠50元,再打6折积分是10%1950.0白金会员 优惠50元,再打7折积分是5%1575.0

 

这就达到了想要的要求。输入一个名字,一个价格,就可以得到结果。

 


优化2: 使用静态工厂,动态获取类

 


动态实例化类: SpringUtils

@Componentpublic class SpringUtils implements ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringUtils.applicatiOnContext= applicationContext;}public static Object getBean(String name) {return applicationContext.getBean(name);}public static T getBean(Class clazz) {return applicationContext.getBean(clazz);}}

 


抽象的策略角色: IDiscount

public interface IDiscount {double compute(double money);
}

 


具体策略角色1 OrdinaryStrategy

@Service
public class OrdinaryStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("普通会员不打折");System.out.println("积分是1%");return money;}}

 


具体策略角色2 PlatinumStrategy

@Service
public class PlatinumStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("白金会员 优惠50元,再打7折");System.out.println("积分是5%");return (money - 50) * 0.7;}}

 


具体策略角色3 GoldStrategy

@Service
public class GoldStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("黄金会员 优惠50元,再打6折");System.out.println("积分是10%");return (money - 50)*0.6;}}

 


工厂方法类  DiscountStrategyFactory

@Servicepublic class DiscountStrategyServiceFactory {private static Map DICOUNT_TYPE = new HashMap<>();static {DICOUNT_TYPE.put("COMMON","goldStrategyService");DICOUNT_TYPE.put("PLATINUM","platinumStrategyService");DICOUNT_TYPE.put("GOLD","goldStrategyService");}public IDiscount getDiscountService(String type){return (IDiscount)SpringUtils.getBean(DICOUNT_TYPE.get(type));}}

 

这边主要用SpringUtils获取bean的方式,实例化type对应的类

 


封装角色; DiscountStrategyServiceContext

@Servicepublic class DiscountStrategyServiceContext {@Resourceprivate  DiscountStrategyServiceFactory discountStrategyServiceFactory;private double getResult(long money, String type) {if (money <1000) {return money;}IDiscount strategy = discountStrategyServiceFactory.getDiscountService(type);if (strategy == null) {throw new IllegalArgumentException("please input right type");}return strategy.compute(money);}public void testResult() {System.out.println(getResult(3300, "GOLD"));System.out.println(getResult(2300, "PLATINUM"));}}

 

 

 


测试; DisountApplicationTests

@RunWith(SpringRunner.class)@SpringBootTest
public class DisountApplicationTests {@Autowiredprivate DiscountStrategyServiceContext discountStrategyServiceContext;@Testpublic void testResult() {discountStrategyServiceContext.testResult();}}

PS:这个是在springBoot项目里面的,启动后 注入@Service,初始化实例

结果:

黄金会员 优惠50元,再打6折积分是10%1950.0白金会员 优惠50元,再打7折积分是5%1575.0

 


总结:

单纯用策略模式,并不能避免使用多重条件判断,结合枚举和工厂方法模式可以解决 问题,只通过一个名字或是数字,匹配到对应的策略。

策略模式的重点就是封装角色,它是借用了代理模式的思路,大家可以想想,它和代理模式有什么差别,差别就是策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口那就成为了代理模式。

 


推荐阅读
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 标题: ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • Hibernate延迟加载深入分析-集合属性的延迟加载策略
    本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。 ... [详细]
  • Ihaveaworkfolderdirectory.我有一个工作文件夹目录。holderDir.glob(*)>holder[ProjectOne, ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
author-avatar
lakensei
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有