不久之前,公司业务重心开始侧重于营销,系统当然要相应的给予支持,于是我花了很多时间去构思这个营销系统需要怎样设计,才能够满足以后各种各样的营销需求。

营销活动的特点无非在于吸引用户去参与并且去完成它,其中除开丰厚的奖励来激励用户,还需要丰富的活动玩法,这像极了电子游戏中任务的设计,在参考了游戏的任务、成就等设计方式之后,我构思了一个可以完成绝大部分营销活动的设计。

核心设计

系统的核心设计在于设计一个任务完成机制,任何活动都是一个任务,活动中的各种动作可以获得不同的分值,分值达到任务的要求之后,就能够获得任务的奖励。所以可以提炼为(以下都以任务来代替营销活动):

  • 任务需要有完成条件,完成条件可以获得分值
  • 任务需要有奖励,并且灵活,达到一定分值可以获得奖励

设计模式

首先设计任务。任务可能会有多个完成条件,每个完成条件的需求值各不相同,比如消费到300、关注公众号(可抽象为0和1)、邀请人注册等。每个任务不能够实时去扫描是否达到完成的要求,所以在完成任务的时候,需要使用观察者模式对任务的动作进行监听。每个任务根据完成条件的需求值注册一些观察者,比如在关注公众号的代码中通知所有观察者、在订单完成的时候通知观察者等。

观察者的触发器分散在各个点,用以触发各种事件:

// 比如在注册时,触发代码就应该为
TriggerManager.trigger("signUp", value);

在观察者触发器内部,会根据事件的不同,利用后文提到的策略模式和工厂模式,对数据库中的所有订阅者进行对应的逻辑:

MarketTriggerRunnerFactory.get(code).run(params);

然后说任务奖励的发放和规则的判断。奖励的发放可以使用策略模式工厂模式,将奖励发放的动作code写入到任务奖励的数据库中,在活动奖励需要发放的时候,可以直接使用:

RewardFactory.getBehavior(code).doReward(userId, rewardItemId, count);

工厂模式就不多赘述,策略模式可以多说两句,因为在Spring的配合下,策略模式非常容易实现,首先定义一个接口,为了举例简单定义个最简化版本:

public interface Behavior<T> {
    String getCode();

    void doReward(String userId, String rewardItemId, String count);
}

针对系统中的每一种奖励赠送,实现一个类:

@Component
public class SendMoneyBehavior implements Behavior<List<BigDecimal>> {
    public String getCode() {
        return "money";
    }

    public void doReward(String userId, String rewardItemId, String count) {
        // ...数据库操作
    }
}

就可以使用工厂模式来实现了:

@Component
public class RewardFactory {

    static Map<String, Behavior> behaviorMap = new HashMap<>();

    @Autowired
    public void setBehavior(Set<Behavior> behaviorSet) {
        for (Behavior behavior : behaviorSet) {
            behaviorMap.put(behavior.getCode(), behavior);
        }
    }

    public static <T> Behavior<T> getBehavior(String code) {
        return behaviorMap.get(code);
    }
}

Spring的IoC会自动把实现了Behavior的类全部注入到RewardFactory,这样就能将赠送奖励的行为添加到数据库中保存,并且以后只需要实现Behavior接口,就能自动拓展奖励发放类型。


再来说说生命周期的设计模式,其实就是用装饰模式来实现的。将生命周期设置在营销活动的各个行为中,达到高度可重用代码和高度灵活配置活动的能力。举一个简单的例子,首先定义装饰器的接口,这一步和上面的策略模式和工厂模式很像:

public interface BehaviorLifecycle<P, T> {
    String getCode();
    void before(Map<String, Object> options);
    void after(Map<String, Object> options);
}

接下来要为赠送的行为增加短信通知、在发送之前扣减库存等行为,只需要实现这个接口,然后在发送的行为之前按堆栈方式执行before,在发送行为之后执行after。如果需要对每个任务进行不同的配置,可以使用策略模式和工厂模式使生命周期也可以在数据库中进行灵活的配置和修改。


这么灵活的设计,对应上千变万化的业务,那么肯定会出现很多参数不统一的情况,针对每个环节可能要对每个实体类进行对应的一些转换,这个时候可以使用上桥接模式

对于实体类,要实现一个我们自定的Transformable接口,这个接口中对应每个环节,都要提供一个抽象方法实现返回所需对应的参数:

public interface Transformable {
    LifecycleParam toLifecycleParam();

    BehaviorParam toBehaviorParam();
}

对于业务中新增的实体,只需要实现这个接口,便可以无缝应用于之前设计好的场景,在之前设计的系统中,使用参数的情况,只需要使用一个桥接器,或者在偷懒的情况下,直接要求传递的参数为Transformable,然后调用对应的方法获取自己需要的参数。举个例子:

public class Car implements Transformable {
    private String id;
    private String name;
    private BigDecimal price;
    private List<String> tags;
    private String desc;
    
    @Override
    public LifecycleParam toLifecycleParam() {
        return new LifecycleParam(id, name, price);
    }

    @Override
    public BehaviorParam toBehaviorParam() {
        return new BehaviorParam(id, name, tags, desc);
    }
}

总结

在使用了设计模式之后,代码的可复用性、系统的灵活性都得到了极大的增强,为后面的功能开发也提供了很多的便利,但是相对的,在代码调试、代码维护、代码理解的工作量也变的更加大了,这是一些难以抵消的缺点,需要开发人员自己来平衡。


0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注