策略模式两种交由spring容器管理的方法

背景介绍

这里举一个我们有购买一个商品,我将用户分为普通用户 、vip用户、超级vip用户。普通用户9.5折,vip用户9折,超级vip用户8折 这种方式来计算用户价格,我们写一下对应的业务代码。

1、定义一下用户类型

public enum UserTypeEnum {
    /**
     * 普通用户
     */
    COMMON_USER(0),

    /**
     * VIP用户
     */
    VIP_USER(1),

    /**
     * 超级VIP用户
     */
    SUPER_VIP_USER(2);


    private int type;

    UserTypeEnum(int type) {
        this.type = type;
    }

    public int getType() {
        return type;
    }

    public static UserTypeEnum getUserEnum(int type) {
        for (UserTypeEnum userTypeEnum : UserTypeEnum.values()) {
            if (userTypeEnum.type == type) {
                return userTypeEnum;
            }
        }
        return null;
    }
}

2、计算价格方法


private static final String NO_USER_TYPE_MSG = "该用户类型没有对应策略";

public BigDecimal quotePrice(BigDecimal orderPrice, int type) {

        UserTypeEnum userEnum = UserTypeEnum.getUserEnum(type);
        if (Objects.isNull(userEnum)) {
            throw new IllegalArgumentException(NO_USER_TYPE_MSG);
        }
        BigDecimal price;
        switch (userEnum) {
            case COMMON_USER:
                price = orderPrice.multiply(new BigDecimal(0.95));
                break;
            case VIP_USER:
                price = orderPrice.multiply(new BigDecimal(0.9));
                break;
            case SUPER_VIP_USER:
                price = orderPrice.multiply(new BigDecimal(0.8));
                break;
            default:
                price = new BigDecimal(-1);
        }

        return price;
    }

其实从这段代码里面,看起来还好,因为每种用户只有一次处理,用户的种类也非常有限。如果是很多确定好的简单方法,这么做并没有什么问题。但我们如果这个计算规则经常变动,可能后续加入积分,优惠券,购买总金额。。等等这些需求的时候。如果一开始采用中方式,大概率是在这个类里面写了很多私有方法。这个类变得越来越臃肿,去改这个方法里面代码需要重新读这个方法里面所有代码,后续越来越难已维护。

策略模式是什么

策略模式定义:
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
实际项目开发中,会有些类仅需要依靠行为就可以区分开来,这时候我们便可以利用封装,使用的时候按实际情况替换即可。

策略模式两种交由spring容器管理的方法_第1张图片
我们在工作中一般是分为几种情况,每种情况建立一个策略类,在用一个上下文将这几种策略和进行统一管理,分为哪种情况直接采用那种策略。

代码实现(由该类本身自己注册到工厂方法中)

1、类型定义用户枚举类(由于在背景介绍已经贴出来了,这里不再贴出)

2、定义工厂管理类

public class UserPayStrategyFactory {

    private static Map userPayBeanMap = new ConcurrentHashMap();

    public  static UserPayService getByUserType(Integer type){
        return userPayBeanMap.get(type);
    }

    public static void register(Integer userType,UserPayService userPayService){
        Assert.notNull(userType,"type can't be null");
        userPayBeanMap.put(userType,userPayService);
    }
}

3、定义接口

public interface UserPayService {

    /**
     * 计算应付价格
     */
    BigDecimal quotePrice(BigDecimal orderPrice);
}

4、定义三种策略具体的实现类
这里是通过InitializingBean方法钩子方法,将对应该类注册到之前定义的工厂管理类中

/**
 *  普通用户的实现
 */
@Service
public class CommonUserPayServiceRegisterImpl implements UserPayService,InitializingBean {

    @Override
    public BigDecimal quotePrice(BigDecimal orderPrice) {
        return orderPrice.multiply(new BigDecimal(0.95));
    }

    @Override
    public void afterPropertiesSet()  {
        UserPayStrategyFactory.register(UserTypeEnum.COMMON_USER.getType(),this);
    }
}


/**
 *  vip接口实现
 */
public class VipUserPayServiceRegisterImpl implements UserPayService,InitializingBean {

    @Override
    public BigDecimal quotePrice(BigDecimal orderPrice) {
        return orderPrice.multiply(new BigDecimal(0.9));
    }

    @Override
    public void afterPropertiesSet()  {
        UserPayStrategyFactory.register(UserTypeEnum.VIP_USER.getType(),this);
    }
}
/**
 *  超级vip接口实现
 */
@Service
public class SuperVipUserPayServiceRegisterImpl implements UserPayService,InitializingBean {

    @Override
    public BigDecimal quotePrice(BigDecimal orderPrice) {
        return orderPrice.multiply(new BigDecimal(0.8));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        UserPayStrategyFactory.register(UserTypeEnum.SUPER_VIP_USER.getType(),this);
    }
}


5、定义具体应用

    @GetMapping("/getUserQuotePriceRegister")
    public String getUserQuotePriceRegister(HttpServletRequest request, HttpServletResponse response) throws IOException {
        UserPayService userService = UserPayStrategyFactory.getByUserType(UserTypeEnum.COMMON_USER.getType());
        BigDecimal quote = userService.quotePrice(new BigDecimal(300));
        System.out.println("普通用户商品的最终价格为_register:" + quote.doubleValue());

        UserPayService userService2 =  UserPayStrategyFactory.getByUserType(UserTypeEnum.VIP_USER.getType());
        BigDecimal quote2 = userService2.quotePrice(new BigDecimal(300));
        System.out.println("vip会员商品的最终价格为_register:" + quote2.doubleValue());

        UserPayService userService3 =  UserPayStrategyFactory.getByUserType(UserTypeEnum.SUPER_VIP_USER.getType());
        BigDecimal quote3 = userService3.quotePrice(new BigDecimal(300));
        System.out.println("SuperVip会员商品的最终价格为_register:" + quote3.doubleValue());
        return "success";
    }

在这里插入图片描述

代码实现(通过ApplicationContextAware钩子方法 中获取对应的bean)

1、定义注解方法

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//如果父类实现该注解,子类也将获得该注解
@Inherited
public @interface UserType {
    UserTypeEnum value();
}

2、类型定义用户枚举类(由于在背景介绍已经贴出来了,这里不再贴出)

3、定义上下文管理bean的工厂

@Component
public class UserTypeContext implements ApplicationContextAware {

    private static final String NO_USER_TYPE_MSG = "该用户类型没有对应策略";

    private static Map> userPayBeanMap = new HashMap<>();


    @Resource
    private ApplicationContext applicationContext;

    /**
     * 将实现 有UserType 注解,实现UserPayService 接口的类放到userPayBeanMap
     * @param applicationContext 上下文
     *
     */

    @Override
    public void setApplicationContext(@Nullable ApplicationContext applicationContext)  {

        Map userTypeMap = applicationContext.getBeansWithAnnotation(UserType.class);
        userTypeMap.forEach((k,v)->{
            if(v instanceof UserPayService){
                Class payServiceClass = ((UserPayService) v).getClass();
                userPayBeanMap.put(payServiceClass.getAnnotation(UserType.class).value().getType(), payServiceClass);
            }
        });
    }

    /**
     * 通过用户类型
     * @param type 用户类型
     * @return  用户类型实现类
     */
    public UserPayService getUserService(Integer type) {
        Class userPayClass = userPayBeanMap.get(type);
        if (Objects.isNull(userPayClass)) {
            throw new IllegalArgumentException(NO_USER_TYPE_MSG);
        }
        return applicationContext.getBean(userPayClass);
    }


}

4、定义接口

public interface UserPayService {

    /**
     * 计算应付价格
     */
    BigDecimal quotePrice(BigDecimal orderPrice);
}

5、定义三种策略具体的实现类
这里是通过打上注解,来标注该策略的类型

/**
 * 普通用户接口实现
 */
@UserType(UserTypeEnum.COMMON_USER)
@Service
public class CommonUserPayServiceImpl implements UserPayService {

    @Override
    public BigDecimal quotePrice(BigDecimal orderPrice) {
        return orderPrice.multiply(new BigDecimal(0.95));
    }

 
}
/**
 *  vip接口实现
 */

@UserType(UserTypeEnum.VIP_USER)
@Service
public class VipUserPayServiceImpl implements UserPayService {

    @Override
    public BigDecimal quotePrice(BigDecimal orderPrice) {
        return orderPrice.multiply(new BigDecimal(0.9));
    }
}
/**
 * 超级 vip接口实现
 */

@UserType(UserTypeEnum.SUPER_VIP_USER)
@Service
public class SuperVipUserPayServiceImpl implements UserPayService {

    @Override
    public BigDecimal quotePrice(BigDecimal orderPrice) {
        return orderPrice.multiply(new BigDecimal(0.8));
    }
}

6、定义具体应用

 @GetMapping("/getUserQuotePrice")
    public String getUserQuotePrice(HttpServletRequest request, HttpServletResponse response) throws IOException {
        UserPayService userService = userTypeContext.getUserService(UserTypeEnum.COMMON_USER.getType());
        BigDecimal quote = userService.quotePrice(new BigDecimal(300));
        System.out.println("普通用户商品的最终价格为:" + quote.doubleValue());

        UserPayService userService2 = userTypeContext.getUserService(UserTypeEnum.VIP_USER.getType());
        BigDecimal quote2 = userService2.quotePrice(new BigDecimal(300));
        System.out.println("vip会员商品的最终价格为:" + quote2.doubleValue());

        UserPayService userService3 = userTypeContext.getUserService(UserTypeEnum.SUPER_VIP_USER.getType());
        BigDecimal quote3 = userService3.quotePrice(new BigDecimal(300));
        System.out.println("SuperVip会员商品的最终价格为:" + quote3.doubleValue());
        return "success";
    }

在这里插入图片描述

两种代码实现的比较

两种方案都是都有一个管理他们本身bean的上下文,一种是用InitializingBean的钩子方法自己注册到factory中。另外一种通过ApplicationContextAware的钩子方法,将实现该注解的bean全部注册自己管理的类中,第二种在我们自己实现策略的时候更加方便,只需要增加一个注解就可以了。第一种需要自己实现InitializingBean将其自身注入进factory中。

你可能感兴趣的:(设计模式,策略模式,spring容器管理,设计模式)