设计模式之策略模式实战

类型:行为型模式

意图:定义一系列算法,不同算法策略可以相互替换,并且互不影响。

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

使用场景:一个系统需要动态地在几种算法中选择一种。

设计模式系列文章目录

角色

  • 策略上下文角色:持有抽象策略角色的引用,访问策略的入口
  • 抽象策略角色
  • 具体策略角色

UML

实战

以抽奖活动发奖为例,奖品多种多样,可能是现金奖,话费奖品,实物奖等等,每种奖品的发放方式都不一样,比如现金是直接转账,话费奖品是调用运营商提供接口发放,实物奖需要人工快递寄送。在未采用策略模式之前,少不了使用 if…else…来判断发放,当增加一种奖品类型时,就需要增加 if 判断。而采取策略模式之后,只需实现一个策略类即可,对原来的逻辑无需做任何改动,也不会影响其他策略的正常逻辑。

本文示例UML图

定义抽象策略接口

/**
 * @author WeJan
 * @since 2020-02-06
 */
public interface PrizeSendStrategy {
    String DEFAULT = "default";
    String MONEY = "money";
    String IN_KIND = "in_kind";
    String CALL_CHARGE = "call_charge";

    String type();

    void doSend();
}

实现具体的策略

@Component
public class CallChargePrizeSendStrategy implements PrizeSendStrategy {
    @Override
    public String type() {
        return CALL_CHARGE;
    }

    @Override
    public void doSend() {
        System.out.println("发放话费奖品");
    }
}

@Component
public class MoneyPrizeSendStrategy implements PrizeSendStrategy {
    @Override
    public String type() {
        return MONEY;
    }

    @Override
    public void doSend() {
        System.out.println("发放现金奖品");
    }
}

@Component
public class InKindPrizeSendStrategy implements PrizeSendStrategy {
    @Override
    public String type() {
        return IN_KIND;
    }

    @Override
    public void doSend() {
        System.out.println("发放实物奖品");
    }
}

@Component
public class EmptyPrizeSendStrategy implements PrizeSendStrategy {
    @Override
    public String type() {
        return DEFAULT;
    }

    @Override
    public void doSend() {
        System.out.println("不发奖");
    }
}

实现对抽象策略封装的上下文对象

/**
 * @author WeJan
 * @since 2020-02-06
 */
public class PrizeSendContext {
    private PrizeSendStrategy prizeSendStrategy;
    public PrizeSendContext() {
    }

    public void setPrizeSendStrategy(PrizeSendStrategy prizeSendStrategy) {
        this.prizeSendStrategy = prizeSendStrategy;
    }

    public void executePrizeSendStrategy() {
        prizeSendStrategy.doSend();
    }
}

抽取策略工厂

客户端需要判断要使用哪一个具体的策略类,若还是按照传统的方法 if…else…来判断策略模式就没有意义了,因此策略模式一般都是结合其他模式共同使用。本文中策略类使用 String 来标识,也可以在策略类中增加抽象方法,返回值为枚举类型。

@Component
public class PrizeSendStrategyFactory implements ApplicationContextAware {
    private static final Map<String, PrizeSendStrategy> PRIZE_SEND_STRATEGY_MAP = new HashMap<>();

    public static PrizeSendStrategy getPrizeSendStrategy(String strategyKey) {
        return PRIZE_SEND_STRATEGY_MAP.getOrDefault(strategyKey, PRIZE_SEND_STRATEGY_MAP.get(DEFAULT));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, PrizeSendStrategy> beans = applicationContext.getBeansOfType(PrizeSendStrategy.class);
        beans.values().forEach(bean -> PRIZE_SEND_STRATEGY_MAP.put(bean.type(), bean));
    }
}

测试

/**
 * @author WeJan
 * @since 2020-02-06
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class StrategyTest {

    @Test
    public void test() {
        PrizeSendContext sendContext = new PrizeSendContext();
        sendContext.setPrizeSendStrategy(PrizeSendStrategyFactory.getPrizeSendStrategy("money"));
        sendContext.executePrizeSendStrategy();
    }

}

输出结果为:

发放现金奖品
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章