案例分析:设计模式与代码的结构特性

我选分析的设计模式是“策略模式”,应用的场景是:部署在网上商城不同品类的商品的不同打折促销手段

一、简单介绍

在软件开发中常常遇到这种情况,实现某一个功能有多种算法或者策略,我们可以根据应用场景的不同选择不同的算法或者策略来完成该功能。把一个类(A)中经常改变或者将来可能改变的部分提取出来,作为一个接口(B),然后在类(A)中包含这个接口(B),这样类(A)的实例在运行时就可以随意调用实现了这个接口的类(C)的行为。比如定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换,使得算法可独立于使用它的客户而变化。这就是策略模式。

二、策略模式的结构

  • 封装类:也叫上下文,对策略进行二次封装,目的是避免高层模块对策略的直接调用。
  • 抽象策略:通常情况下为一个接口,当各个实现类中存在着重复的逻辑时,则使用抽象类来封装这部分公共的代码,此时,策略模式看上去更像是模版方法模式。
  • 具体策略:具体策略角色通常由一组封装了算法的类来担任,这些类之间可以根据需要自由替换。

三、策略模式对系统架构和代码结构带来的好处

  • 上下文和具体策略是松耦合关系。因此上下文只知道它要使用某一个实现Strategy接口类的实例,但不需要知道具体是哪一个类。
  • 策略模式满足“开-闭原则”。当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例。

四、Demo

     1、抽象策略类:PromotionStrategy

package strategy;

public interface PromotionStrategy {
    void doPromotion(); //不同的促销策略
}

     2、具体策略类

package strategy;

public class FanXianPromotionStrategy implements PromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("返现促销,返回的金额存放到用户的余额中");
    }
}

 

package strategy;

public class LiJianPromotionStrategy implements PromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("立减促销,课程的价格直接减去配置的价格");
    }
}

 

package strategy;

public class ManJianPromotionStrategy implements PromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("满减促销,满200-20元");
    }
}

   3、封装工具类(应用工厂模式,封装对策略的选择)

package strategy;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 李宇刚 on 2019-11-23
 */
public class PromotionStrategyFactory {
    private static Map<String, PromotionStrategy> promotionStrategy_Map = new HashMap<>();
    static {
        promotionStrategy_Map.put(PromotionKey.FANXIAN, new FanXianPromotionStrategy());
        promotionStrategy_Map.put(PromotionKey.LIJIAN, new LiJianPromotionStrategy());
        promotionStrategy_Map.put(PromotionKey.MANJIAN, new ManJianPromotionStrategy());
    }

    private PromotionStrategyFactory(){}

    public static PromotionStrategy getPromotionStrategy(String promotionKey) {
        PromotionStrategy promotionStrategy = promotionStrategy_Map.get(promotionKey);
        if (promotionStrategy == null)
            return null;
        else
            return promotionStrategy;
    }

    private interface PromotionKey{
        String LIJIAN = "LIJIAN";
        String FANXIAN = "FANXIAN";
        String MANJIAN = "MANJIAN";
    }

}

   4、封装应用类

package strategy;

public class PromotionActivity {

    private PromotionStrategy promotionStrategy;

    public PromotionActivity(PromotionStrategy promotionStrategy) {

        this.promotionStrategy = promotionStrategy;
    }

    public void executePromotionStrategy(){

        promotionStrategy.doPromotion();
    }

}

   5、应用场景

package strategy;

public class Test {
    public static void main(String[] args) {

        String promotionKey = "MANJIAN";
        PromotionActivity promotionActivity = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey));
        promotionActivity.executePromotionStrategy();

    }
}

运行结果:

满减促销,满200-20元

五、解释其中用到的多态机制

 

从图中可以看出,三种促销策略都实现了PromotionStrategy接口。在向封装应用类中传递具体策略时,使用的是接口类型作为形式参数,在此处使用了多态,再结合工厂模式实现对具体策略的选择,减少了大部分不必要的if……else……判断条件,这也是策略模式的优势所在。

六、说明模块抽象封装的方法 

本代码实现封装的功能体现在封装应用类 PromotionActivity 上面,将具体的PromotionStrategy策略作为私有属性,通过创建新的PromotionActivity实例类来部署具体的PromotionStrategy,并且通过具体类 executePromotionStrategy 来执行 PromotionStrategy 需要实现的方法。

七、 分析各个模块的内聚度和模块之间的耦合度

以下为这个项目的UML类图

 

可以看到,通过运用策略模式,整个项目的内聚程度很高,耦合程度很低。

具体为:将 PromotionStrategy 作为功能接口,利用多态具体实现不同的促销策略。在写使用类的时候,使用的是 PromotionStrategy 作为形式参数,这样能够极大程度使得使用类和具体功能拆分开。当增添了新的促销策略类时,使用类不需要改动一行代码。各模块聚焦于自身事务,相互不干扰。

 

八、 应用范例源代码

github版本库URL:https://github.com/lygttxs/strategy.git

 

posted @ 2019-11-25 16:13  lggang  阅读(275)  评论(0编辑  收藏  举报