设计模式之策略模式

策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern),其定义如下:Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组
算法,将每个算法都封装起来,并且使它们之间可以互换。)-设计模式之禅

案例:

一家商场在年底推出活动,全场商品一律打八折,部分商品在全场打八折的基础上在七折,然后金额满300减20.使用策略模式怎么来设计这个场景

不管是八折 七折 还是满减 都是不同的算法机制。首先定义一个计算的接口出来。

package com.zcm.strategy2;

import java.math.*;

/**
 * @Description:计算
 * 一家商场在年底推出活动,
 * 全场商品一律打八折,
 * 部分商品作为特价处理,
 * 部分商品在全场打八折的基础上在七折,
 * 然后实付金额满300减20.使用策略模式怎么来设计这个场景
 * @Author: zcm
 * @Version:v.2.3.0
 * @Date:2021/1/23 15:13
 */
public interface PriceIStrategy<T> {
    /**
     * @Description:计算价格并返回
     * @Author: zcm
     * @Version:v.2.3.0
     * @Date:2021/1/23 15:12
     */
    BigDecimal compute(T t);
}

全场八折的算法【第一种策略算法】

package com.zcm.strategy2;

import java.math.*;

/**
 * @program: DemoZCM
 * @ClassName Audience
 * @Description
 * @Author zcm
 * @Date 2021/1/23 15:21
 * @Version V1.0
 */
public class AudienceEight implements PriceIStrategy<Goods> {
    @Override
    public BigDecimal compute(Goods goods) {
        BigDecimal price = goods.getPrice();
        return price.multiply(new BigDecimal(0.8)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}

七折的算法【第二种策略算法】

package com.zcm.strategy2;

import java.math.*;

/**
 * @program: DemoZCM
 * @ClassName AudienSeven
 * @Description
 * @Author zcm
 * @Date 2021/1/23 15:24
 * @Version V1.0
 */
public class AudienceSeven implements PriceIStrategy<Goods>{
    @Override
    public BigDecimal compute(Goods goods) {
        BigDecimal price = goods.getPrice();
        return price.multiply(new BigDecimal(0.7)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
}

满减的算法【第三种策略算法】

package com.zcm.strategy2;

import java.math.*;

/**
 * @program: DemoZCM
 * @ClassName FullReduction
 * @Description
 * @Author zcm
 * @Date 2021/1/23 15:30
 * @Version V1.0
 */
public class FullReduction implements PriceIStrategy<Goods> {
    @Override
    public BigDecimal compute(Goods goods) {
        BigDecimal price = goods.getPrice();
        if (price.compareTo(new BigDecimal(300)) > 0) {
            BigDecimal subtract = price.subtract(new BigDecimal(20));
            return subtract;
        } else {
            return goods.getPrice();
        }
    }
}

实体类

/***********************************************************************
 * Module:  Album.java
 * Author:  Administrator
 * Purpose: Defines the Class Album
 ***********************************************************************/
package com.zcm.strategy2;


import lombok.*;

import java.math.*;
import java.util.*;

/**
 * 相册
 *
 * @pdOid 6cdc5177-846d-4273-b22f-47a6ec081c42
 */
@Data
public class Goods {

    private String id;
    /**
     * @Description:价格
     * @Author: zcm
     * @Version:v.2.3.0
     * @Date:2021/1/23 15:17
     */
    protected BigDecimal price;
    /**
    *@Description:商品名
    *@Author: zcm
    *@Version:v.2.3.0
    *@Date:2021/1/23 15:17
    */
    protected String name;

    public Goods(String id, BigDecimal price, String name) {
        this.id = id;
        this.price = price;
        this.name = name;
    }

    public Goods() {
    }
}

封装策略的上下文,也可已在这个里面去组装复杂的算法

package com.zcm.strategy2;

import java.math.*;

/**
 * @program: DemoZCM
 * @ClassName GoodsPriceComparator
 * @Description
 * @Author zcm
 * @Date 2021/1/23 15:40
 * @Version V1.0
 */
public class StrategyContext {
    private PriceIStrategy priceIStrategy;

    public StrategyContext(PriceIStrategy priceIStrategy) {
        this.priceIStrategy = priceIStrategy;
    }

    public StrategyContext() {
    }

    /**
     * @Description:计算商品价格
     * @Author: zcm
     * @Version:v.2.3.0
     * @Date:2021/1/23 16:06
     */
    public Goods comparator(Goods goods) {
        BigDecimal price = this.priceIStrategy.compute(goods);
        goods.setPrice(price);
        return goods;
    }
}

使用 Context 来查看当它改变策略 Strategy 时的行为变化。

package com.zcm.strategy2;

import java.math.*;

/**
 * @program: DemoZCM
 * @ClassName Test
 * @Description
 * @Author zcm
 * @Date 2021/1/23 15:25
 * @Version V1.0
 */
public class Test {
    public static void main(String[] args) {
        StrategyContext context = new StrategyContext(new AudienceEight());
        BigDecimal price = new BigDecimal(599);
        System.out.println("商品原价:" + price);
        /**
         * 全场打八折
         */
        System.out.println("--------------------------全场打八折-----------------------------");

        Goods goods = context.comparator(new Goods("123", price, "连帽羽绒服"));
        System.out.println("打完八折后的商品价格:" + goods.getName() + ",价格:" + goods.getPrice());
        /**
         * 在打八折的基础上再打七折
         */
        System.out.println("-----------------------在打八折的基础上再打七折----------------------");
        context = new StrategyContext(new AudienceSeven());
        goods = context.comparator(goods);
        System.out.println("八折的基础上在打七折后的商品价格:" + goods.getName() + ",价格:" + goods.getPrice());
        /**
         * 最后满300减20
         */
        System.out.println("--------------------------最后满300减20--------------------------");
        context = new StrategyContext(new FullReduction());
        goods = context.comparator(goods);
        System.out.println("最后满300减20后的商品价格:" + goods.getName() + ",价格:" + goods.getPrice());


    }
}

为什么使用策略模式?

在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

使用策略模式有什么优缺点?

优点:

1、算法可以*切换。

这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封 装角色对其进行封装,保证对外提供“可*切换”的策略

2、避免使用多重条件判断。

没有策略模式的情况下,加入需要加减乘除,使用多重的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略
家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。

3、扩展性良好。

想要增加一种新的算法,只需要实现策略接口就好了。

缺点:

1.策略类会增多。

每一个策略都是一个类,复用的可能性很小,类数量增多。

2.所有的策略类都需要对外暴露。

上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。

使用场景:

1.多个类只要在算法或者行为上面相似的场景。

2.算法*切换的场景。

3. 需要屏蔽算法规则的场景。
 

注意事项:如果一个系统的策略多于4个,就需要考虑使用混合模式,解决策略类膨胀的问题。

总结:

策略模式是一个非常简单的模式。它在项目中使用得非常多,但它单独使用的地方就比较少了,因为它有致命缺陷:所有的策略都需要暴露出去,这样才方便客户端决定使用哪一个策略

 

 

上一篇:Myeclipse10传智书城实验项目


下一篇:鱿鱼游戏玻璃桥模拟