在某个电商体系中,有若干个营销活动,每个营销活动的后台操作基本相同。为了便于管理,节约维护成本和未来的开发成本,于是开发了一套活动后台管理系统,统一管理所有活动的后台设置。

  三个营销活动a、b,相同的基本操作add(), remove(),importData() 等,针对每个操作,处理类的核心逻辑为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private AService aService;

private BService bService;

// 中间省略

//根据活动类型,选择具体的活动处理service及方法
switch(activityType){
case 1 :
return aService.list();
case 2 :
return bService.list();
default :
break;
}

  很显然,上述代码臃肿,若要接入新活动,必须修改代码,违背了开闭原则。下面开始重构。

  我发现每个营销活动都有唯一的活动类型,即一个 activityType 对应一个 activityService 实例,符合简单工厂模式的定义。接下来,定义模型:

  • Factory(工厂角色):根据外界指令,创建所需要的产品对象。即根据 activityType 得到 activityService
  • Product(抽象产品角色):工厂类所生产对象的父类,封装了所有产品的共有方法。即 add(), remove() 等。
  • ConcreateProduct(具体产品角色):简单工厂模式的创建目标,需要实现抽象产品角色的所有抽象方法。即 AService、BService、CService
  • 抽象产品类 AbstractActivity
1
2
3
4
5
6
7
8
public abstract class AbstractActivity {

public abstract void add();

public abstract void remove();

public abstract void importData();
}
  • 具体产品类 AService、BService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class AService extends AbstractActivity {

    @Override
    public void add() {

    }

    @Override
    public void remove() {

    }

    @Override
    public void importData() {

    }
    }

    public class BService extends AbstractActivity {

    @Override
    public void add() {

    }

    @Override
    public void remove() {

    }

    @Override
    public void importData() {

    }
    }
  • 工厂角色类 Factory

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Factory {

    public static Product getProduct(Integer activityType) {

    if (null == activity) {
    // 省略
    }

    AbstractActivity activity = null;

    if (activityType.equals(1)) {
    activity = new AService();
    } else if (activityType.equals(2)) {
    activity = new BService();
    }

    return activity;
    }
    }
  • 模拟客户端 Client

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Client {
    public static void main(String args[]) {

    Integer activityType = 1;
    //通过工厂类创建产品对象
    AbstractActivity activity = Factory.getProduct(activityType);
    activity.list();
    }
    }

  我成功地利用多态实现了请求分发功能。此时又多了一种营销活动 c,我们要在工厂类中增加 if-else if 分支,此举违背了 开闭原则。

1
2
3
4
5
6
7
if (activityType.equals(1)) {
activity = new AService();
} else if (activityType.equals(2)) {
activity = new BService();
} else if (activityType.equals(3)) {
activity = new CService();
}

我们可以通过 context.getBeansOfType 获得所有产品角色,来构建工厂。
  • 工厂类代码片段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //得到所有继承了AbstractActivity的实例 -> 得到所有具体产品角色
    Map<String, AbstractActivity> activityMap = context.getBeansOfType(AbstractActivity.class);
    //创建抽象类工厂 I<产品类型, 产品角色>
    Map<Integer, AbstractActivity> factoryMap = new HashMap<>(activityMap.size());

    for (Map.Entry<String, AbstractActivity> entry : activityMap.entrySet()) {
    AbstractActivity activity = entry.getValue();
    //产品角色中自带 类型
    factoryMap.put(activity.getType(), activity);
    }
  • 抽象产品类 AbstractActivity

1
2
3
4
5
6
7
8
9
10
11
public abstract class AbstractActivity {

//产品类型
public abstract Integer getType();

public abstract void add();

public abstract void remove();

public abstract void importData();
}
  • 具体产品类 AService
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class AService extends AbstractActivity {

    @Override
    public Integer getType() {
    //每个产品对应不同的类型
    return 1;
    }

    @Override
    public void add() {

    }

    @Override
    public void remove() {

    }

    @Override
    public void importData() {

    }
    }

  开发新的产品,只需要继承抽象产品角色即可,不需要再修改其他代码片段。

  此时有了一个新问题,新产品 d 没有 importData() 方法。产品 d 不需要重写 抽象方法。抽象方法和调用者不能直接接入,需要中转适配——适配器模式。

  我引入接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了里面所有方法,而我们不和原始的接口交互,只和抽象类关联,再写一个类继承抽象类,重写我们需要的方法即可。

  • 接口类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public interface IActivityService {

    Integer getType();

    void add();

    void remove();

    void importData();
    }
  • 抽象产品

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public abstract class AbstractActivity implements IActivityService {

    public Integer getType() {
    return null;
    }

    public void add() {

    }

    public void remove() {

    }

    public void importData() {

    }

    }
  • 具体产品

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class AService extends AbstractActivity {

    @Override
    public Integer getType() {
    //每个产品对应不同的类型
    return 1;
    }

    @Override
    public void add() {

    }

    @Override
    public void remove() {

    }

    @Override
    public void importData() {

    }
    }

    public class DService extends AbstractActivity {

    @Override
    public Integer getType() {
    //每个产品对应不同的类型
    return 1;
    }

    @Override
    public void add() {

    }

    @Override
    public void remove() {

    }
    }

  即使此时需要为多个产品增加公共方法,只在interface 和 abstract 中增加新方法,且在需要使用新方法的产品类中重写即可,对其它产品类是透明的。