为了方便我们从Spring中获取UserPayService的各个策略类,我们创建一个工厂类:
- /**
- * @author mhcoding
- */
- public class UserPayServiceStrategyFactory {
-
- private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>();
-
- public static UserPayService getByUserType(String type){
- return services.get(type);
- }
-
- public static void register(String userType,UserPayService userPayService){
- Assert.notNull(userType,"userType can't be null");
- services.put(userType,userPayService);
- }
- }
这个UserPayServiceStrategyFactory中定义了一个Map,用来保存所有的策略类的实例,并提供一个getByUserType方法,可以根据类型直接获取对应的类的实例。还有一个register方法,这个后面再讲。
有了这个工厂类之后,计算价格的代码即可得到大大的优化:
- /**
- * @author mhcoding
- */
- public BigDecimal calPrice(BigDecimal orderPrice,User user) {
-
- String vipType = user.getVipType();
- UserPayService strategy = UserPayServiceStrategyFactory.getByUserType(vipType);
- return strategy.quote(orderPrice);
- }
以上代码中,不再需要if-else了,拿到用户的vip类型之后,直接通过工厂的getByUserType方法直接调用就可以了。
通过策略+工厂,我们的代码很大程度的优化了,大大提升了可读性和可维护性。
但是,上面还遗留了一个问题,那就是UserPayServiceStrategyFactory中用来保存所有的策略类的实例的Map是如何被初始化的?各个策略的实例对象如何塞进去的呢?
Spring Bean的注册
还记得我们前面定义的UserPayServiceStrategyFactory中提供了的register方法吗?他就是用来注册策略服务的。
接下来,我们就想办法调用register方法,把Spring通过IOC创建出来的Bean注册进去就行了。
这种需求,可以借用Spring种提供的InitializingBean接口,这个接口为Bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。
那么,我们将前面的各个策略类稍作改造即可:
- /**
- * @author mhcoding
- */
- @Service
- public class ParticularlyVipPayService implements UserPayService,InitializingBean {
-
- @Override
- public BigDecimal quote(BigDecimal orderPrice) {
- if (消费金额大于30元) {
- return 7折价格;
- }
- }
-
- @Override
- public void afterPropertiesSet() throws Exception {
- UserPayServiceStrategyFactory.register("ParticularlyVip",this);
- }
- }
-
- @Service
- public class SuperVipPayService implements UserPayService ,InitializingBean{
-
- @Override
- public BigDecimal quote(BigDecimal orderPrice) {
- return 8折价格;
- }
-
- @Override
- public void afterPropertiesSet() throws Exception {
- UserPayServiceStrategyFactory.register("SuperVip",this);
- }
- }
-
- @Service
- public class VipPayService implements UserPayService,InitializingBean {
-
- @Override
- public BigDecimal quote(BigDecimal orderPrice) {
- if(该用户超级会员刚过期并且尚未使用过临时折扣){
- 临时折扣使用次数更新();
- returen 8折价格;
- }
- return 9折价格;
- }
-
- @Override
- public void afterPropertiesSet() throws Exception {
- UserPayServiceStrategyFactory.register("Vip",this);
- }
- }
只需要每一个策略服务的实现类都实现InitializingBean接口,并实现其afterPropertiesSet方法,在这个方法中调用UserPayServiceStrategyFactory.register即可。
这样,在Spring初始化的时候,当创建VipPayService、SuperVipPayService和ParticularlyVipPayService的时候,会在Bean的属性初始化之后,把这个Bean注册到UserPayServiceStrategyFactory中。
以上代码,其实还是有一些重复代码的,这里面还可以引入模板方法模式进一步精简,这里就不展开了。
还有就是,UserPayServiceStrategyFactory.register调用的时候,第一个参数需要传一个字符串,这里的话其实也可以优化掉。比如使用枚举,或者在每个策略类中自定义一个getUserType方法,各自实现即可。
总结
本文,我们通过策略模式、工厂模式以及Spring的InitializingBean,提升了代码的可读性以及可维护性,彻底消灭了一坨if-else。
文中的这种做法,大家可以立刻尝试起来,这种实践,是我们日常开发中经常用到的,而且还有很多衍生的用法,也都非常好用。有机会后面再介绍。
其实,如果读者们对策略模式和工厂模式了解的话,文中使用的并不是严格意义上面的策略模式和工厂模式。
首先,策略模式中重要的Context角色在这里面是没有的,没有Context,也就没有用到组合的方式,而是使用工厂代替了。
另外,这里面的UserPayServiceStrategyFactory其实只是维护了一个Map,并提供了register和get方法而已,而工厂模式其实是帮忙创建对象的,这里并没有用到。
(编辑:ASP站长网)
|