策略模式与案例运用

策略模式

写在前面

策略模式不是解决if else 的,不是用于分支选择的。
策略模式的前提是已知所选哪个分支。
在我理解看来策略模式其实只是使得代码更加整洁,合理。或者是说让业务代码(客户端代码context)与算法(具体的实现or strategy)隔离开来。

一、策略模式的用法

(一)策略模式有3个东西:

  1. context (调用策略接口方法的环境类)他会维护一个Strategy的引用,用于调用实际的接口方法
  2. strategy (策略接口)定义支持所有算法的公共接口
  3. XXStrategy (具体策略实现类)实现Strategy接口中的具体算法

(二)案例解说

核销平台接入若干个业务方,我们需要要某些场景调用业务方提供的pigeon service

例如:我需要调用业务方的根据id查询商品信息的方法,但是各个业务方的实现不同。我需要根据商品类型调用不同的业务方方法。

改造前

代码(一)根据id查找商品信息

public getProductInfoByProductIdAndType(Long productId, Integer type) {
   if (type == 1) {
   		//丽人BU
       beautyBiz.getProductInfoByProductId(productId);
   } else if (type == 2) {
   		//休娱BU
       entertainBiz.getProductInfoByProductId(productId);
   } else {...}
}

以上是之前的代码,且除了这个方法以外,还有很多类似的其他的方法。可以看出算法与业务耦合在一起了。


改造后

根据策略模式的思路,新建了一个strategy接口,再新建了2个具体实现策略类(假设目前有2个业务方接入)。其实还可以写一个AbstractBaseStrategy的抽象类实现Strategy接口,实现所有接口方法的默认实现,好处有二:1、提取公共代码 2、有些业务方,没有提供某些接口方法

代码(二)具体策略类

//丽人策略类
public class BeautyStrategy implements Strategy {


    @Override
    public ProductDTO getProductByIdAndType(Long aLong) {
        return null;
    }

    @Override
    public ProductDTO getProductByIdAndVersion(Long aLong, String s) {
        return null;
    }
    。。。
}


//休娱策略类
public class EntertainStrategy implements Strategy {


    @Override
    public ProductDTO getProductByIdAndType(Long aLong) {
        return null;
    }

    @Override
    public ProductDTO getProductByIdAndVersion(Long aLong, String s) {
        return null;
    }
}

那么此时context又如何写呢?其实跟之前差不多,可能看起来整洁些了。

代码(三)context环境类

public class context {
    public ProductDTO getProductInfoByProductIdAndType (Long productId, Integer type) {
        Strategy strategy  = null;
        if (type == 1) {
            strategy = new BeautyStrategy();
        } else if (type == 2) {
            strategy = new EntertainStrategy();
        }
        
        strategy.getProductById(productId);
    }
    ...
}

或者还可以用spring bean的方式事先装配好这些service的对象。


如何去掉if else 呢?

(一)通过HashMap

首先准备好type与strategy的对应关系Map。

这个就有多种方法:

1、如果使用spring,那么就需要在context里写一个map,这个map可以在xml里配置,在成员变量处自动装配;或者在构造函数里装配,但是依据spring版本的不同,构造函数是否传入参数有异议。以下配置是xml:

<bean id="typeStrategyMap" class="java.util.HashMap">
        <constructor-arg>
            <map key-type="java.lang.Integer">
                <entry key="1" value-ref="beautyStrategy"/>
                <entry key="4" value-ref="beautyStrategy"/>
            </map>
        </constructor-arg>
</bean>

前提是beautyStrategy也得声明好bean,可通过注解或者是beanxml。

这时,context代码如下写法:

@Resource
private Map<Integer, Strategy> typeStrategyMap;
public ProductDTO getProductByIdAndType(Long productId, int type) {
        if (productId == null) {
            return null;
        } else {
            Map<Integer, Strategy> typeStrategyMap = this.getTypeStrategyMap();
            //校验类型
            if (!this.typeValidate(typeStrategyMap, type)) {
                return null;
            } else {
                ProductDTO productDTO = null;
                //通过具体策略调用相应业务方的方法
                Strategy strategy = (Strategy)typeStrategyMap.get(type);
                if (strategy != null) {
                    productDTO = strategy.getProductByIdAndType(productId);
                }

                return null != productDTO && type == productDTO.getProductType().intValue() ? productDTO : null;
            }
        }
    }

说白了,很简单,就是用map.get方法来去掉所有的if else

(二)通过枚举类 + 工厂方式

枚举类的用法与map有些许差异,但是本质是一样的,将type与具体的策略关联起来。

1、枚举类

待测试


public enum TypeStrategyEnum {
    beauty_strategy(1, new TestStrategy());

    private int type;
    private Strategy strategy;

    TypeStrategyEnum(int type, Strategy strategy) {
        this.type = type;
        this.strategy = strategy;
    }
}

public enum TypeStrategyEnum {
    beauty_strategy(1, "beautyStrategy");

    private int type;
    private String strategy;

    TypeStrategyEnum(int type, String strategy) {
        this.type = type;
        this.strategy = strategy;
    }
}

2、context

(1)利用反射,来动态创建具体的策略对象


public class Context{
    public ProductDTO getProductInfoByProductIdAndType(Long productId, Integer type) {
        //根据type拿到枚举类
        TypeStrategyEnum typeStrategyEnum = TypeStrategyEnum.getEnumByType(type);

        //利用工厂
        Strategy strategy = (Strategy) Class.forName(typeStrategyEnum.getStrategy()).newInstance();
        strategy.getProductByIdAndType(productId);
    }
}

(2)通过java代码获取bean

获取bean的几种方式
譬如说可以在代码里读取xml配置,然后根据名称拿到bean对象,或者通过其他方式。。。

posted @ 2017-10-16 23:05  jennyjj  阅读(1092)  评论(0编辑  收藏  举报