策略模式 – 设计模式之行为模式
目录
定义:
类图:
例子:
类图:
抽象的策略角色: 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折
public interface IDiscount {double compute(double money);
}
public class OrdinaryStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("普通会员不打折");System.out.println("积分是1%");return money;}}
public class PlatinumStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("白金会员 优惠50元,再打7折");System.out.println("积分是5%");return (money - 50) * 0.7;}}
public class GoldStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("黄金会员 优惠50元,再打6折");System.out.println("积分是10%");return (money - 50)*0.6;}}
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);}}}
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 进行判断,具体使用哪个策略。这个要怎么进行优化呢?只要知道一个名字就可以了,传递相关的数字或是名字进来,反馈一个结果,这才是想要的。
传递名字: 考虑用枚举,如例子中,三种会员的类型,相对固定。
对各个策略进行集中处理,考虑用工厂方法模式来实现策略类的声明。要知道策略的名称,接口那需要添加一个获取类型的方法。
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;}}
public interface IDiscount {double compute(double money);// 用于匹配类型String getType();}
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();}}
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();}}
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();}}
public class DiscountStrategyFactory {private Map
或是用静态的方式,直接map,这样就不用声明getType() 和实现这个方法了
DiscountStrategyFactoryStatic:
public class DiscountStrategyFactoryStatic {public static Map
}
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
这就达到了想要的要求。输入一个名字,一个价格,就可以得到结果。
@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
public interface IDiscount {double compute(double money);
}
@Service
public class OrdinaryStrategy implements IDiscount {@Overridepublic double compute(double money) {System.out.println("普通会员不打折");System.out.println("积分是1%");return money;}}
@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;}}
@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;}}
@Servicepublic class DiscountStrategyServiceFactory {private static Map
这边主要用SpringUtils获取bean的方式,实例化type对应的类
@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"));}}
@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
单纯用策略模式,并不能避免使用多重条件判断,结合枚举和工厂方法模式可以解决 问题,只通过一个名字或是数字,匹配到对应的策略。
策略模式的重点就是封装角色,它是借用了代理模式的思路,大家可以想想,它和代理模式有什么差别,差别就是策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口那就成为了代理模式。