策略模式详解以及在spring中策略模式的两种简化应用

本文详细介绍了策略模式的概念及其与简单工厂模式的区别,通过实例展示了如何在Spring中使用策略模式,强调了策略模式在解耦和简化单元测试上的优势,并提出了通过反射进一步优化策略选择的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

策略模式,同样可以消除掉繁杂的if else ,那么它和简单工厂模式的区别在哪?
简单工厂模式是根据传入的参数返回不同的产品,不同的产品内部封装了不同的逻辑。

策略模式类似,但是策略模式内部是封装了算法,也就是封装了某一种策略。
举个例子,超市活动打折优惠,1.普通计费。 2.满300减20 3.满400减80 4.满500打八折
如果用简单工厂模式,那么需要定义四个产品子类,如果将来又新增了类型, 比如说 5.满800减200 ,那么又需要定义一个新的产品子类。

仔细观察会发现,其实 2.满300减20 和3.满400减80 ,是同样的算法,只是参数不同。

然后满500打8折和满800减200,是也是一样的算法,只是参数不同。
因此,我们可以把这两类算法 分为 满减算法和打折算法。
满减算法

//参数为 原金额,优惠门槛,优惠金额
double  moneyOff(double d1,double d2,double d3){
if(d1>=d2){
return d1-d3;
}else{
return d1;
}
}

打折算法

//参数为 原金额  打折折扣
duble discount (double d1,double d2){
return d1*d2;
}

策略模式是对算法进行的封装,简单工厂是对不同行为模式或者属性的对象进行的封装,比如说水果工厂, 会产出苹果对象,香蕉对象,他们的形状大小功能各不相同。

简单的来说,策略模式里面,封装了众多算法,但是这些算法 最终都是要为了做同一件事,比如说上文的 计算优惠价。

策略模式的uml类图:
在这里插入图片描述

以上的具体实现如下
策略接口

/**
 * @Author laixiaoxing
 * @Description 策略接口
 * @Date 下午7:19 2019/6/8
 */
public interface Strategy {
    /**
     * 计算金额
     * 接受原金额,返回打折后的金额
     * @return
     */
    double althmCash(double cash);
}

正常金额策略


/**
 * @ClassName DiscountstrategyImpl
 * @Author laixiaoxing
 * @Date 2019/6/8 下午7:23
 * @Description 正常计算金额的策略类
 * @Version 1.0
 */
public class NomalstrategyImpl implements Strategy {

    @Override
    public double althmCash(double cash) {

        return cash;
    }
}

优惠满减策略


/**
 * @ClassName MoneyOffStrategyImpl
 * @Author laixiaoxing
 * @Date 2019/6/8 下午7:25
 * @Description 优惠满减策略类
 * @Version 1.0
 */
public class MoneyOffStrategyImpl implements Strategy {


    /**
     * 优惠金额
     */
    private double discountMoney;

    /**
     * 满减金额
     */
    private double fullMoney;

    public MoneyOffStrategyImpl(double discountMoney,double fullMoney) {
        this.discountMoney = discountMoney;
        this.fullMoney=fullMoney;
    }

    @Override
    public double althmCash(double cash) {
        if (cash >= fullMoney) {
            return cash - discountMoney;
        } else {
            return cash;
        }
    }
}


打折优惠策略


/**
 * @ClassName DiscountstrategyImpl
 * @Author laixiaoxing
 * @Date 2019/6/8 下午7:23
 * @Description 计算打折优惠的策略类
 * @Version 1.0
 */
public class DiscountstrategyImpl implements Strategy {

    private double discount;

    public DiscountstrategyImpl(double discount) {
        this.discount = discount;
    }

    @Override
    public double althmCash(double cash) {

        return cash*discount;
    }
}

策略模式context 维护策略模式的引用 ,
避免将策略模式对象暴露出去,面试对象的对外封闭,外部调用的时候只能通过context来调用。 这样的话,修改stragety的时候,就不需要去修改具改调用方的代码。


/**
 * @ClassName context
 * @Author laixiaoxing
 * @Date 2019/6/8 下午7:29
 * @Description 策略模式的上下文,维护对strategy对象的引用
 * @Version 1.0
 */
public class Context {

    private Strategy strategy;


    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public double strategyAlthm(double cansh) {
        return strategy.althmCash(cansh);
    }
}

实际调用的时候

 public static void main(String[] args) {

        Context context;

        String type = "满300减20";
        switch (type) {
            case "满300减20":
                context = new Context(new MoneyOffStrategyImpl(20, 300));
                break;

            case "满400减80":
                context = new Context(new MoneyOffStrategyImpl(80, 400));
                break;

            case "满500打八折":
                context = new Context(new DiscountstrategyImpl(0.8));
                break;
            default:
                context = new Context(new NomalstrategyImpl());
        }

        double result = context.strategyAlthm(300);

        System.out.println("result:"+result);

    }

这个时候,我们会发现,又回到了当初if else的时候,极其不友好。

优化一下,将context和简单工厂模式结合

结合后的context

public class Context {

    private Strategy strategy;


    public Context(String type) {
        switch (type) {
            case "满300减20":
                strategy = new MoneyOffStrategyImpl(20, 300);
                break;

            case "满400减80":
                strategy = new MoneyOffStrategyImpl(80, 400);
                break;

            case "满500打八折":
                strategy = new DiscountstrategyImpl(0.8);
                break;
            default:
                strategy = new NomalstrategyImpl();
        }

    }

    public double strategyAlthm(double cansh) {
        return strategy.althmCash(cansh);
    }
}




调用方

  public static void main(String[] args) {

        String type = "满300减20";
        Context context = new Context(type);

        double result = context.strategyAlthm(300);

        System.out.println("result:" + result);
    }

和工厂模式的比较:
1.工厂模式封装的是对象,策略模式封装的是算法
2.工厂模式可能需要将工厂和产品都暴露给调用方,因为调用方可能会用到产品的不同方面。
但是策略模式,只需要将context暴露给调用方,其内部算法对调用方不可见,不需要将算法子类暴露出去,因为这些算法,本质上都是完成同一件事的不同方法。

工厂模式的链接:
https://blog.youkuaiyun.com/qq_20009015/article/details/91058102

策略模式的优点:
1.将使用算法的类和封装算法类,进行了解耦,简化了单元测试,可以通过自己的接口进行单独的单元测试,
2.只要是在不同的时间场合应用不同的业务规则,就可以考虑使用策略模式,而不是将多个行为堆积到一个类里面,可以很好的消费条件语句。

我们可以看到,现在不管是在context还是在factory里面,都封装了switch,非常不方便,一旦要修改策略或者产品,就需要改掉这一块。 可以通过反射,将这些都消除掉。

例如:
spring中的应用 ,在bean加载完之后启动的时候,将所有接口的实现子类都加载到一个map中,key为类型,value该类型对应的策略对象,然后在context里面,直接根据map里面的key去获取对象。

具体如下,
实现ApplicationContextAware接口,重写setApplicationContext方法,参数为ApplicationContext ,这样就可以拿到spring的ApplicationContext,这个是spring的上下文,里面有spring加载的所有对象信息。

最后通过applicationContext.getBeansOfType(clazz); 返回值是一个map,里面就是对应的class的对象,如果clazz是一个接口的类型,那么返回的就是这个接口的实现类。

/**
 * @ClassName ApplicationContextHelper
 * @Author laixiaoxing
 * @Date 2019/3/13 下午4:09
 * @Description 获取springbean的工具类
 * @Version 1.0
 */
@Component
public class ApplicationContextHelper implements ApplicationContextAware {

    ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    public <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

    public <T> Map<String, T> getBeansOfType(Class<T> clazz) {
        return applicationContext.getBeansOfType(clazz);
    }


}

或者
参考这个文章https://mp.weixin.qq.com/s/K0eTW7jvQlY6XToYw8z0mw

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值