设计模式在美团外卖营销业务中的实践
Posted 分布式实验室
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式在美团外卖营销业务中的实践相关的知识,希望对你有一定的参考价值。
开闭原则(Open Closed Principle,OCP)
单一职责原则(Single Responsibility Principle, SRP)
里氏代换原则(Liskov Substitution Principle,LSP)
依赖倒转原则(Dependency Inversion Principle,DIP)
接口隔离原则(Interface Segregation Principle,ISP)
合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
最少知识原则(Least Knowledge Principle,LKP)或者迪米特法则(Law of Demeter,LOD)
返奖金额的计算,涉及到不同的计算规则。
从邀请开始到返奖结束的整个流程。
新用户
普通奖励(给予固定金额的奖励)
梯度奖(根据用户邀请的人数给予不同的奖励金额,邀请的人越多,奖励金额越多)
老用户
根据老用户的用户属性来计算返奖金额。为了评估不同的邀新效果,老用户返奖会存在多种返奖机制。
计算完奖励金额以后,还需要更新用户的奖金信息,以及通知结算服务对用户的金额进行结算。这两个模块对于所有的奖励来说都是一样的。
//抽象的产品
public abstract class Product {
public abstract void method();
}
//定义一个具体的产品 (可以定义多个具体的产品)
class ProductA extends Product {
@Override
public void method() {} //具体的执行逻辑
}
//抽象的工厂
abstract class Factory<T> {
abstract Product createProduct(Class<T> c);
}
//具体的工厂可以生产出相应的产品
class FactoryA extends Factory{
@Override
Product createProduct(Class c) {
Product product = (Product) Class.forName(c.getName()).newInstance();
return product;
}
}
//定义一个策略接口
public interface Strategy {
void strategyImplementation();
}
//具体的策略实现(可以定义多个具体的策略实现)
public class StrategyA implements Strategy{
@Override
public void strategyImplementation() {
System.out.println("正在执行策略A");
}
}
//封装策略,屏蔽高层模块对策略、算法的直接访问,屏蔽可能存在的策略变化
public class Context {
private Strategy strategy = null;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void doStrategy() {
strategy.strategyImplementation();
}
}
//抽象策略
public abstract class RewardStrategy {
public abstract void reward(long userId);
public void insertRewardAndSettlement(long userId, int reward) {} ; //更新用户信息以及结算
}
//新用户返奖具体策略A
public class newUserRewardStrategyA extends RewardStrategy {
@Override
public void reward(long userId) {} //具体的计算逻辑,...
}
//老用户返奖具体策略A
public class OldUserRewardStrategyA extends RewardStrategy {
@Override
public void reward(long userId) {} //具体的计算逻辑,...
}
//抽象工厂
public abstract class StrategyFactory<T> {
abstract RewardStrategy createStrategy(Class<T> c);
}
//具体工厂创建具体的策略
public class FactorRewardStrategyFactory extends StrategyFactory {
@Override
RewardStrategy createStrategy(Class c) {
RewardStrategy product = null;
try {
product = (RewardStrategy) Class.forName(c.getName()).newInstance();
} catch (Exception e) {}
return product;
}
}
public class RewardContext {
private RewardStrategy strategy;
public RewardContext(RewardStrategy strategy) {
this.strategy = strategy;
}
public void doStrategy(long userId) {
int rewardMoney = strategy.reward(userId);
insertRewardAndSettlement(long userId, int reward) {
insertReward(userId, rewardMoney);
settlement(userId);
}
}
}
public class InviteRewardImpl {
//返奖主流程
public void sendReward(long userId) {
FactorRewardStrategyFactory strategyFactory = new FactorRewardStrategyFactory(); //创建工厂
Invitee invitee = getInviteeByUserId(userId); //根据用户id查询用户信息
if (invitee.userType == UserTypeEnum.NEW_USER) { //新用户返奖策略
NewUserBasicReward newUserBasicReward = (NewUserBasicReward) strategyFactory.createStrategy(NewUserBasicReward.class);
RewardContext rewardContext = new RewardContext(newUserBasicReward);
rewardContext.doStrategy(userId); //执行返奖策略
}if(invitee.userType == UserTypeEnum.OLD_USER){} //老用户返奖策略,...
}
}
在接收到订单消息后,用户进入待校验状态;
在校验后,若校验通过,用户进入预返奖状态,并放入延迟队列。若校验未通过,用户进入不返奖状态,结束流程;
T+N天后,处理延迟消息,若用户未退款,进入待返奖状态。若用户退款,进入失败状态,结束流程;
执行返奖,若返奖成功,进入完成状态,结束流程。若返奖不成功,进入待补偿状态;
待补偿状态的用户会由任务定期触发补偿机制,直至返奖成功,进入完成状态,保障流程结束。
//定义一个抽象的状态类
public abstract class State {
Context context;
public void setContext(Context context) {
this.context = context;
}
public abstract void handle1();
public abstract void handle2();
}
//定义状态A
public class ConcreteStateA extends State {
@Override
public void handle1() {} //本状态下必须要处理的事情
@Override
public void handle2() {
super.context.setCurrentState(Context.contreteStateB); //切换到状态B
super.context.handle2(); //执行状态B的任务
}
}
//定义状态B
public class ConcreteStateB extends State {
@Override
public void handle2() {} //本状态下必须要处理的事情,...
@Override
public void handle1() {
super.context.setCurrentState(Context.contreteStateA); //切换到状态A
super.context.handle1(); //执行状态A的任务
}
}
//定义一个上下文管理环境
public class Context {
public final static ConcreteStateA contreteStateA = new ConcreteStateA();
public final static ConcreteStateB contreteStateB = new ConcreteStateB();
private State CurrentState;
public State getCurrentState() {return CurrentState;}
public void setCurrentState(State currentState) {
this.CurrentState = currentState;
this.CurrentState.setContext(this);
}
public void handle1() {this.CurrentState.handle1();}
public void handle2() {this.CurrentState.handle2();}
}
//定义client执行
public class client {
public static void main(String[] args) {
Context context = new Context();
context.setCurrentState(new ContreteStateA());
context.handle1();
context.handle2();
}
}
//返奖状态执行的上下文
public class RewardStateContext {
private RewardState rewardState;
public void setRewardState(RewardState currentState) {this.rewardState = currentState;}
public RewardState getRewardState() {return rewardState;}
public void echo(RewardStateContext context, Request request) {
rewardState.doReward(context, request);
}
}
public abstract class RewardState {
abstract void doReward(RewardStateContext context, Request request);
}
//待校验状态
public class OrderCheckState extends RewardState {
@Override
public void doReward(RewardStateContext context, Request request) {
orderCheck(context, request); //对进来的订单进行校验,判断是否用券,是否满足优惠条件等等
}
}
//待补偿状态
public class CompensateRewardState extends RewardState {
@Override
public void doReward(RewardStateContext context, Request request) {
compensateReward(context, request); //返奖失败,需要对用户进行返奖补偿
}
}
//预返奖状态,待返奖状态,成功状态,失败状态(此处逻辑省略)
//..
public class InviteRewardServiceImpl {
public boolean sendRewardForInvtee(long userId, long orderId) {
Request request = new Request(userId, orderId);
RewardStateContext rewardContext = new RewardStateContext();
rewardContext.setRewardState(new OrderCheckState());
rewardContext.echo(rewardContext, request); //开始返奖,订单校验
//此处的if-else逻辑只是为了表达状态的转换过程,并非实际的业务逻辑
if (rewardContext.isResultFlag()) { //如果订单校验成功,进入预返奖状态
rewardContext.setRewardState(new BeforeRewardCheckState());
rewardContext.echo(rewardContext, request);
} else {//如果订单校验失败,进入返奖失败流程,...
rewardContext.setRewardState(new RewardFailedState());
rewardContext.echo(rewardContext, request);
return false;
}
if (rewardContext.isResultFlag()) {//预返奖检查成功,进入待返奖流程,...
rewardContext.setRewardState(new SendRewardState());
rewardContext.echo(rewardContext, request);
} else { //如果预返奖检查失败,进入返奖失败流程,...
rewardContext.setRewardState(new RewardFailedState());
rewardContext.echo(rewardContext, request);
return false;
}
if (rewardContext.isResultFlag()) { //返奖成功,进入返奖结束流程,...
rewardContext.setRewardState(new RewardSuccessState());
rewardContext.echo(rewardContext, request);
} else { //返奖失败,进入返奖补偿阶段,...
rewardContext.setRewardState(new CompensateRewardState());
rewardContext.echo(rewardContext, request);
}
if (rewardContext.isResultFlag()) { //补偿成功,进入返奖完成阶段,...
rewardContext.setRewardState(new RewardSuccessState());
rewardContext.echo(rewardContext, request);
} else { //补偿失败,仍然停留在当前态,直至补偿成功(或多次补偿失败后人工介入处理)
rewardContext.setRewardState(new CompensateRewardState());
rewardContext.echo(rewardContext, request);
}
return true;
}
}
过滤规则大部分可重用,但也会有扩展和变更。
不同资源位的过滤规则和过滤顺序是不同的。
同一个资源位由于业务所处的不同阶段,过滤规则可能不同。
//定义一个抽象的handle
public abstract class Handler {
private Handler nextHandler; //指向下一个处理者
private int level; //处理者能够处理的级别
public Handler(int level) {
this.level = level;
}
public void setNextHandler(Handler handler) {
this.nextHandler = handler;
}
// 处理请求传递,注意final,子类不可重写
public final void handleMessage(Request request) {
if (level == request.getRequstLevel()) {
this.echo(request);
} else {
if (this.nextHandler != null) {
this.nextHandler.handleMessage(request);
} else {
System.out.println("已经到最尽头了");
}
}
}
// 抽象方法,子类实现
public abstract void echo(Request request);
}
// 定义一个具体的handleA
public class HandleRuleA extends Handler {
public HandleRuleA(int level) {
super(level);
}
@Override
public void echo(Request request) {
System.out.println("我是处理者1,我正在处理A规则");
}
}
//定义一个具体的handleB
public class HandleRuleB extends Handler {} //...
//客户端实现
class Client {
public static void main(String[] args) {
HandleRuleA handleRuleA = new HandleRuleA(1);
HandleRuleB handleRuleB = new HandleRuleB(2);
handleRuleA.setNextHandler(handleRuleB); //这是重点,将handleA和handleB串起来
handleRuleA.echo(new Request());
}
}
//定义一个抽象的规则
public abstract class BasicRule<CORE_ITEM, T extends RuleContext<CORE_ITEM>>{
//有两个方法,evaluate用于判断是否经过规则执行,execute用于执行具体的规则内容。
public abstract boolean evaluate(T context);
public abstract void execute(T context) {
}
//定义所有的规则具体实现
//规则1:判断服务可用性
public class ServiceAvailableRule extends BasicRule<UserPortrait, UserPortraitRuleContext> {
@Override
public boolean evaluate(UserPortraitRuleContext context) {
TakeawayUserPortraitBasicInfo basicInfo = context.getBasicInfo();
if (basicInfo.isServiceFail()) {
return false;
}
return true;
}
@Override
public void execute(UserPortraitRuleContext context) {}
}
//规则2:判断当前用户属性是否符合当前资源位投放的用户属性要求
public class UserGroupRule extends BasicRule<UserPortrait, UserPortraitRuleContext> {
@Override
public boolean evaluate(UserPortraitRuleContext context) {}
@Override
public void execute(UserPortraitRuleContext context) {
UserPortrait userPortraitPO = context.getData();
if(userPortraitPO.getUserGroup() == context.getBasicInfo().getUserGroup().code) {
context.setValid(true);
} else {
context.setValid(false);
}
}
}
//规则3:判断当前用户是否在投放城市,具体逻辑省略
public class CityInfoRule extends BasicRule<UserPortrait, UserPortraitRuleContext> {}
//规则4:根据用户的活跃度进行资源过滤,具体逻辑省略
public class UserPortraitRule extends BasicRule<UserPortrait, UserPortraitRuleContext> {}
//我们通过Spring将这些规则串起来组成一个一个请求链
<bean name="serviceAvailableRule" class="com.dianping.takeaway.ServiceAvailableRule"/>
<bean name="userGroupValidRule" class="com.dianping.takeaway.UserGroupRule"/>
<bean name="cityInfoValidRule" class="com.dianping.takeaway.CityInfoRule"/>
<bean name="userPortraitRule" class="com.dianping.takeaway.UserPortraitRule"/>
<util:list id="userPortraitRuleChain" value-type="com.dianping.takeaway.Rule">
<ref bean="serviceAvailableRule"/>
<ref bean="userGroupValidRule"/>
<ref bean="cityInfoValidRule"/>
<ref bean="userPortraitRule"/>
</util:list>
//规则执行
public class DefaultRuleEngine{
@Autowired
List<BasicRule> userPortraitRuleChain;
public void invokeAll(RuleContext ruleContext) {
for(Rule rule : userPortraitRuleChain) {
rule.evaluate(ruleContext)
}
}
}
以上是关于设计模式在美团外卖营销业务中的实践的主要内容,如果未能解决你的问题,请参考以下文章