营销业务本身极具复杂多变性,特别是伴随着数字化营销蓬勃发展的趋势,在市场的不同时期、公司发展的不同阶段、面向不同的用户群体以及持续效果波动迭代,都会产生不同的营销策略决策。
当面对随时变化的业务场景时,系统的扩展性就显得非常重要。而在谈到系统设计扩展性的时候,总是首先会想到设计原则和设计模式。但设计模式不是银弹,并不能解决所有问题,它只是前人提炼总结出来的招式方法,需要开发者根据实际业务场景进行合理的选择、合适的变通,才能真正去解决实际场景中的问题,并总结形成自己的方法论。
那么接下来我们看看设计模式是如何帮助我们在营销策略引擎中提升系统扩展性的。
先简单介绍一下营销策略引擎:策略引擎是通过搭建可视化流程组件,定义各个流程节点,自动化执行活动业务流程,从而提供不同运营活动能力。其中核心活动业务流程主要包括三大部分:运营活动配置->运营活动审批->运营活动执行。
具体场景
模式分析
在GoF《设计模式:可复用面向对象软件的基础》中:工厂模式被分成了工厂方法和抽象工厂两类,而简单工厂模式(又称静态工厂模式)被看作是工厂方法的一种特例。不过由于简单工厂和工厂方法相对更简单和易于理解,代码可读性也更强,因此在实际项目中更加常用。
其中简单工厂的适用场景:
而工厂方法的适用场景:
典型代码示例
//抽象产品类publicabstractclassProduct{publicabstractvoidmethod();}//具体的产品类classProductAextendsProduct{@Overridepublicvoidmethod(){//具体的执行逻辑}}//抽象工厂模板类abstractclassFactory
在实际项目代码中我们采用的是简单工厂模式(静态工厂模式),实现时利用枚举(或者映射配置表)来保存渠道类型与具体策略实现类的映射关系,再结合Spring的单例模式,来进行策略类的创建。
相比于工厂方法模式,在满足业务的前提下,减少了工厂类数量,代码更加简单适用。
在创建不同类型运营活动策略的时候,可以发现除了保存具体活动渠道配置信息不一样之外,创建过程中很多操作流程是相同的:比如保存活动基本配置信息,审计日志上报,创建活动审批工单,创建完成后消息提醒等。
原有实践
对于每种活动策略而言,这些操作都是必需的且操作流程都是固定的,所以可以将这些操作提取成公用的流程,此时就考虑到了模板方法模式。
在GoF《设计模式:可复用面向对象软件的基础》:模板方法模式是在一个方法中定义一个算法骨架,并将某些步骤推迟到其子类中实现。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。
模板方法模式适用场景:业务逻辑由确定的步骤组成,这些步骤的顺序要是固定不变的,不同的具体业务之间某些方法或者实现可以有所不同。
实现时一般通过抽象类来定义一个逻辑模板和框架,然后将无法确定的部分抽象成抽象方法交由子类来实现,调用逻辑仍在抽象类中完成。
//模板类publicabstractclassAbstractTemplate{//业务逻辑1protectedabstractvoiddoStep1();//业务逻辑2protectedabstractvoiddoStep2();//模板方法publicvoidtemplateMethod(){this.doStep1();//公共逻辑......this.doStep2();}}//具体实现类1publicclassConcreteClass1extendsAbstractTemplate{//实现业务逻辑1protectedvoiddoStep1(){//业务逻辑处理}//实现业务逻辑2protectedvoiddoStep2(){//业务逻辑处理}}//具体实现类2publicclassConcreteClass2extendsAbstractTemplate{//实现业务逻辑1protectedvoiddoStep1(){//业务逻辑处理}//实现业务逻辑2protectedvoiddoStep2(){//业务逻辑处理}}//调用类publicclassClient{publicstaticvoidmain(String[]args){AbstractTemplateclass1=newConcreteClass1();AbstractTemplateclass2=newConcreteClass2();//调用模板方法class1.templateMethod();class2.templateMethod();}}实际代码
模板方法模式有两大作用:复用和扩展。复用是指所有的子类可以复用父类中提供的模板方法的代码。扩展是指框架通过模板模式提供功能扩展点,让用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
模板方法非常适用于有通用业务逻辑处理流程,同时又在具体流程上存在一定差异的场景,可以通过将流程骨架抽取到模板类中,将可变的差异点设置为抽象方法,达到封装不变部分,扩展可变部分的目的。
上述我们通过模板方法模式抽取出了公共流程骨架,但这里还存在一个问题:调用类仍需要明确知道具体实现类是哪个,实例化后才可进行调用。也就是每一次增加新的渠道活动时,调用方都必须修改调用逻辑,添加新的活动实现类的初始化调用,显然不利用业务的扩展性。
在创建运营活动过程中,不同类型的活动会对应着不同的创建流程,调用方只需要根据渠道类型来进行区分,而无需理会其中具体的业务逻辑。此时策略模式是一个比较好的选择。
在GoF《设计模式:可复用面向对象软件的基础》中:策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的调用方。
//策略接口定义publicinterfaceStrategy{voiddoStrategy();}//策略具体实现类(多个)publicclassStrategyAimplementsStrategy{@OverridepublicvoiddoStrategy(){}}//上下文操作类,屏蔽高层模块对策略的直接访问publicclassContext{privateStrategystrategy=null;publicContext(Strategystrategy){this.strategy=strategy;}publicvoiddoStrategy(){strategy.doStrategy();}}实际代码
实践总结
策略模式在项目开发过程中经常用于消除复杂的ifelse复杂逻辑,后续如果有新的渠道活动时,只需要新增对应渠道的活动创建逻辑即可,可以十分便捷地对系统业务进行扩展。
在项目实践过程,经常会将工厂模式、模板方法模式和策略模式一起结合使用。模板方法模式进行业务流程公共骨架的抽取,策略模式进行具体子流程策略的实现和调用的封装,而工厂模式可以进行子流程策略的创建。
多种模式的结合使用可以充分发挥出各个模式的优势,达到真正提升系统设计扩展性的目的。
在运营活动的执行过程中,会涉及活动状态的变更,以及变更前的条件检测和变更后的操作处理。与之相对应地,我们很容易就会想到状态模式。
在GoF经典的《设计模式:可复用面向对象软件的基础》中:状态模式允许一个对象在其内部状态改变的时候改变其行为。
状态模式的作用就是分离状态的行为,通过维护状态的变化,来调用不同状态对应的不同功能。它们的关系可以描述为:状态决定行为。由于状态是在运行期被改变的,因此行为也会在运行期随着状态的改变而改变。
/***状态模式*抽象状态类**/interfaceState{//状态对应的处理voidhandle()}//具体状态关现类publicclassConcreteStateAimplementsState{@Overridepublicvoidhandle(){}}publicclassConcreteStateBimplementsState{@Overridepublicvoidhandle(){}}//环境类Context,访问入口publicclassContext{//持有一个State类型的对象实例privateStatestate;publicvoidsetState(Statestate){this.state=state;}publicvoidrequest(){//转调state来处理state.handle();}}publicclassClient{publicstaticvoidmain(String[]args){//创建状态Statestate=newConcreteStateB();//创建环境Contextcontext=newContext();//将状态设置到环境中context.setState(state);//请求context.request();}}实践总结
在实际软件项目开发中,业务状态不多且状态转移简单的场景,可使用状态模式来实现;但如果是涉及的业务流程状态转移繁杂时,使用状态模式会引入非常多的状态类和方法,当状态逻辑有变更时,代码也会变得难以维护,此时使用状态模式并不十分适合。
而当流程状态繁多,事件校验和触发执行动作包含的业务逻辑比较复杂时,如何去实现呢?
这里我们必须停下来思考:使用设计模式只是解决实际问题的一种手段,但设计模式不是一把“万能的”锤子,需要清楚地了解到它的优势和不足。而这种问题场景下,业界已经有一个更通用的方案——有限状态机,通过更高层的封装,提供给业务更便捷的应用。
有限状态机(Finite-StateMachine,缩写:FSM),业界简称状态机。它亦是由事件、状态、动作三大部分组成,三者的关系是:事件触发状态的转移,状态的转移触发后续动作的执行。状态机可以基于传统的状态模式硬编码来实现,也可以通过数据库/文件配置或者DSL的方式来保存状态及转移配置来实现(推荐)。
业界中也已涌现出了不少开源状态机的框架,比较常用的有Spring-statemachine(Spring官方提供)、squirrelstatemachine和阿里开源的cola-statemachine。
实际应用
实践代码
实际场景中,不必强行套用设计模式,而是应当充分结合业务的特点,同时针对设计模式的优劣势,进行更加合适的选型或者进一步扩展。
为了做好品质和风险管控,活动创建需要加入审批环节,把控运营活动的发布执行,同时对于不同类型的运营活动,可能涉及的业务领域和部门各不相同,审批管控人员也不一样,需要配置相对应的审批关系。
此时需要做到:
针对这方面的需求,业界有一套通用的业务工具——工作流引擎。工作流引擎显然并不属于具体某一种设计模式的实现,它是涵盖了多种设计模式的组件应用。
不仅仅是审批功能,其实前面自动化营销流程引擎设计也同样是使用工作流引擎搭建流程组件:
状态机VS工作流引擎
工作流引擎和状态机似乎存在非常多的相似之处,都可以通过定义流程的节点、转移条件和相应触发的操作来完成业务流程。如果只从适用场景的复杂性上看,状态机更适用于单维度的业务问题,能够清晰地描绘出所有可能的状态以及导致转换的事件,更加灵活轻便;而工作流引擎则更适合业务流程管理,解决如大型CRM复杂度更高的流程自动化问题,可以改善整体业务流程的效率。
工作流引擎流程图:
工作流引擎是涵盖了多种设计模式的应用组件,只有在复杂多变的业务场景中才需要应用,需要结合业务进行仔细评估。在合适的场景使用合适的解决方案,遵循系统架构设计的简单、合适、可演化原则,不过度设计。