2020-03-23

美团设计模式在外卖营销业务中的实践-学习笔记(一)

美团设计模式在外卖营销业务中的实践-学习笔记(一)一、设计模式原则二、设计模式在美团外卖营销业务中的具体案例2.1、工厂模式和策略模式2.1.1 业务简介2.1.2 返奖规则与设计模式业务建模模式:工厂模式模式:策略模式工程实践:2.1.3 返奖流程与设计模式实践业务建模工程实践

看了美团技术团队的 设计模式在外卖营销业务中的实践 一文后的一些记录,emmm为了证明我看过,哈哈,还有最后一个责任链模式暂时还不知道怎么弄。。。 感兴趣的可以点这里去看看原文,干货满满。第一次写学习笔记,不知道写的好不好,欢迎各位大佬评论交流,大家一起学习啊。

一、设计模式原则

面向对象的设计模式有七大基本原则:

开闭原则 (Open Closed Principle, OCP)

单一职责原则(Single Responseibility 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)

简单理解就是:开闭原则是总纲,它指导我们要对扩展开放,对修改关闭;单一职责原则则指导我们实现类要职责单一;里氏替换原则知道我们不要破坏继承体系;依赖倒置原则指导我们要面向接口编程;接口隔离原则指导我们在设计接口的时候要精简单一;迪米特法则则指导我们要降低耦合。

二、设计模式在美团外卖营销业务中的具体案例

2.1、工厂模式和策略模式

学习设计模式或者是在工程中实践设计模式,必须深入到某一个特定的业务场景中去,再结合对业务场景的理解和领域模型的建立,才能体会到设计模式思想的精髓。在这里美团结合了其“邀请下单”业务来进行设计模式实践与分享。

2.1.1 业务简介

“邀请下单”是美团外面用户邀请其他用户下单后给予奖励的平台。即用户A邀请用户B,并且用户B在美团下单后给予用户A一定的现金奖励。同时为了协调成本与收益的关系,返奖会有多个计算策略。邀请下单后台主要涉及两个技术要点:

返奖金额计算,涉及到不同的计算原则。

从邀请开始到返奖结束的整个流程。

2.1.2 返奖规则与设计模式

业务建模

模式:工厂模式

// 抽象的产品

publicabstractclassProduct{

publicabstractvoidmethod();

}

// 产品A

publicclassProductAextendsProduct{

@Override

publicvoidmethod() {

System.out.println("This is ProductA");

   }

}

// 产品B

publicclassProductBextendsProduct{

@Override

publicvoidmethod() {

System.out.println("This is Product B");

   }

}

// 创建一个抽象的工厂

publicabstractclassFactory{

publicabstractProductcreateProduct(Classc)throwsException;

}

// 具体分工厂实现 (注意 这里知识命名为FactoryA,不是ProductA的专属工厂)

publicclassFactoryAextendsFactory{

@Override

publicProductcreateProduct(Classc)throwsException{

// 利用反射动态创建实例

return(Product)Class.forName(c.getName()).newInstance();

   }

}

// 具体使用

publicstaticvoidmain(String[]args)throwsException{

      //创建工厂

FactoryAfactoryA=newFactoryA();

      // 使用工厂创建具体的产品

Productproduct=factoryA.createProduct(ProductA.class);

product.method();

Productproduct1=factoryA.createProduct(ProductB.class);

product1.method();

}

// 输出结果

ConnectedtothetargetVM,address:'127.0.0.1:65319',transport:'socket'

ThisisProductA

ThisisProductB

DisconnectedfromthetargetVM,address:'127.0.0.1:65319',transport:'socket'

Processfinishedwithexitcode0

模式:策略模式

// 定义一个策略接口

publicinterfaceStrategy{

voidstrategyImplementation();

}

// 具体的策略实现

publicclassStrategyAimplementsStrategy{

@Override

publicvoidstrategyImplementation() {

System.out.println("正在执行策略A");

   }

}

publicclassStrategyBimplementsStrategy{

@Override

publicvoidstrategyImplementation() {

System.out.println("正在执行策略B");

   }

}

// 策略封装 屏蔽高层模块对策略、算法的直接访问 使用context同一操作

publicclassContext{

privateStrategystrategy=null;

publicContext(Strategystrategy){

this.strategy=strategy;

   }

publicvoiddoStrategy(){

strategy.strategyImplementation();

   }

}

// 具体使用

publicstaticvoidmain(String[]args)throwsException{

StrategyAstrategy=newStrategyA();

ContextcontextA=newContext(strategy);

contextA.doStrategy();

StrategyBstrategyB=newStrategyB();

ContextcontextB=newContext(strategyB);

contextB.doStrategy();

   }

// 输出结果

ConnectedtothetargetVM,address:'127.0.0.1:65488',transport:'socket'

正在执行策略A

正在执行策略B

DisconnectedfromthetargetVM,address:'127.0.0.1:65488',transport:'socket'

Processfinishedwithexitcode0


工程实践:

// 抽象策略

publicabstractclassRewardStrategy{

// 生成的返奖金额 不同策略实现不同 定义成抽象的 由子类自己实现

publicabstractintreward(longuserId);

  // 更新账户及结算信息 每个用户和返奖规则最后都要执行 统一实现

publicvoidinsertRewardAndSettlement(longuserId,intreward){

System.out.println("更新用户信息以及结算成功:userId => "+userId+",reward => "+reward);

   }

}

// 新用户返奖策略 (这里使用随机数模拟返奖金额 )

publicclassNewUserRewardStrategyextendsRewardStrategy{

@Override

publicintreward(longuserId) {

System.out.println("新用户反奖策略,用户 => "+userId);

return(int)(Math.random()*10);

   }

}

// 老用户返奖策略 (这里也使用随机数模拟返奖金额 )

publicclassOldUserRewardStrategyextendsRewardStrategy{

@Override

publicintreward(longuserId) {

System.out.println("老用户反奖策略,用户 => "+userId);

return(int)(Math.random()*10);

   }

}

// 抽象工厂

publicabstractclassStrategyFactory{

abstractRewardStrategycreateStrategy(Classc);

}

// 具体的工厂 (根据具体的类生成不同的策略)

publicclassFactorStrategyFactoryextendsStrategyFactory{

@Override

publicRewardStrategycreateStrategy(Classc) {

RewardStrategystrategy=null;

try{

strategy=(RewardStrategy)Class.forName(c.getName()).newInstance();

}catch(Exceptione){

e.printStackTrace();

       }

returnstrategy;

   }

}

// 使用策略模式来执行具体的策略

publicclassRewardContext{


privateRewardStrategystrategy;


  // 构造方法 传入具体到的策略

publicRewardContext(RewardStrategystrategy){

this.strategy=strategy;

   }

publicvoiddoStrategy(longuserId){

intreward=strategy.reward(userId);

strategy.insertRewardAndSettlement(userId,reward);

   }

}

// 具体使用 使用时没有直接对策略、算法的直接访问 而是通过context进行操作

// 这里使用随机数的大小来判断 使用 老用户策略还是新用户策略

publicstaticvoidmain(String[]args) {

FactorStrategyFactoryfactory=newFactorStrategyFactory();

RewardContextcontext;

RewardStrategystrategy;

doublei=Math.random();

System.out.println(i);

if(i>0.4){

strategy=factory.createStrategy(OldUserRewardStrategy.class);

context=newRewardContext(strategy);

context.doStrategy(123456);

}else{

strategy=factory.createStrategy(NewUserRewardStrategy.class);

context=newRewardContext(strategy);

context.doStrategy(456789);

       }

}

// 执行结果

ConnectedtothetargetVM,address:'127.0.0.1:49241',transport:'socket'

0.4496623743530703// 这个是生成的随机数

老用户反奖策略,用户=>123456

更新用户信息以及结算成功:userId=>123456,reward=>3

DisconnectedfromthetargetVM,address:'127.0.0.1:49241',transport:'socket'

Processfinishedwithexitcode0

业务建模

// 定义一个抽象的状态类

publicabstractclassState{

protectedStateContextcontext;

publicvoidsetContext(StateContextcontext){

this.context=context;

   }

publicabstractvoidhandle1();

publicabstractvoidhandle2();

}

// 定义A状态

publicclassConcreteStateAextendsState{

@Override

publicvoidhandle1() {

System.out.println("执行状态1。。。");

   }

@Override

publicvoidhandle2() {

//切换为状态B

super.context.setCurrentState(StateContext.concreteStateB);

//执行状态B的任务

super.context.handle2();

   }

}

// 定义B状态

publicclassConcreteStateBextendsState{

@Override

publicvoidhandle1() {

//切换回状态A

super.context.setCurrentState(StateContext.cincreteStateA);

//执行状态A的任务

super.context.handle1();

   }

@Override

publicvoidhandle2() {

System.out.println("正在执行状态B");

   }

}

// 定义一个上下文管理环境

publicclassStateContext{

publicfinalstaticConcreteStateBconcreteStateB=newConcreteStateB();

publicfinalstaticConcreteStateAconcreteStateA=newConcreteStateA();

privateStatecurrentState;

publicStategetCurrentState(){

returncurrentState;

   }

publicvoidsetCurrentState(StatecurrentState){

this.currentState=currentState;

this.currentState.setContext(this);

   }

publicvoidhandle1() {this.currentState.handle1();}

publicvoidhandle2() {this.currentState.handle2();}

}

// 使用示例

publicstaticvoidmain(String[]args) {

StateContextcontext=newStateContext();

context.setCurrentState(newCincreteStateA());

context.handle1();

context.handle2();

}

// 运行结果 (在A状态中转换成状态B并执行状态B的方法)

ConnectedtothetargetVM,address:'127.0.0.1:49837',transport:'socket'

执行状态1。。。

正在执行状态B

DisconnectedfromthetargetVM,address:'127.0.0.1:49837',transport:'socket'

Processfinishedwithexitcode0

工程实践

// 返奖状态执行的上下文

publicclassStateContext{

privateStatestate;

publicvoidsetState(Statestate){

this.state=state;

   }

publicStategetState(){returnstate;}

publicvoidecho(StateContextcontext){

state.doReward(context);

   }

publicbooleanisResultFlag(){

returnstate.isResultFlag();

   }

}

// 返奖状态抽象类

publicabstractclassState{

// 具体执行

publicabstractvoiddoReward(StateContextcontext);

  // 判断是否通过改判断

publicabstractbooleanisResultFlag();

}

// 各状态下的处理逻辑 根据上面的业务建模来实现的 这里还是用随机数来模拟流程是否执行成功

// 订单状态检查

publicclassCheckOrderStateextendsState{

privatebooleanflag=false;

@Override

publicvoiddoReward(StateContextcontext) {

System.out.println(context.getClass().getName());

System.out.println("CheckOrderState 订单状态检查...");

doublei=Math.random();

if(i>0.4){

flag=true;

       }

   }

@Override

publicbooleanisResultFlag() {

returnflag;

   }

}

// 预返奖检查

publicclassBeforeRewardCheckStateextendsState{

privatebooleanflag=false;

@Override

publicvoiddoReward(StateContextcontext) {

System.out.println(context.getClass().getName());

System.out.println("BeforeRewardCheckState 预反奖状态检查...");

doublei=Math.random();

if(i>0.4){

flag=true;

       }

   }

@Override

publicbooleanisResultFlag() {

returnflag;

   }

}

// 返奖流程

publicclassSendRewardCheckStateextendsState{

privatebooleanflag=false;

@Override

publicvoiddoReward(StateContextcontext) {

System.out.println(context.getClass().getName());

System.out.println("SendRewardCheckState 待反奖状态检查...");

doublei=Math.random();

if(i>0.4){

flag=true;

       }

   }

@Override

publicbooleanisResultFlag() {

returnflag;

   }

}

// 补偿放奖流程

publicclassCompentstateRewardStateextendsState{

privatebooleanflag=false;

@Override

publicvoiddoReward(StateContextcontext) {

System.out.println(context.getClass().getName());

System.out.println("CompentstateRewardState 补偿反奖状态...");

doublei=Math.random();

if(i>0.4){

flag=true;

       }

   }

@Override

publicbooleanisResultFlag() {

returntrue;

   }

}

// 返奖失败状态

publicclassRewardFailStateextendsState{

@Override

publicvoiddoReward(StateContextcontext) {

System.out.println(context.getClass().getName());

System.out.println("RewardFailState 反奖失败状态...");

   }

@Override

publicbooleanisResultFlag() {

returnfalse;

   }

}

// 返奖成功状态

publicclassRewardSuccessStateextendsState{

@Override

publicvoiddoReward(StateContextcontext) {

System.out.println(context.getClass().getName());

System.out.println("RewardSuccessState 反奖成功状态...");

   }

@Override

publicbooleanisResultFlag() {

returnfalse;

   }

}

// 全部流程整合

publicstaticvoidmain(String[]args) {

dosomething();

   }

publicstaticbooleandosomething(){

StateContextcontext=newStateContext();

context.setState(newCheckOrderState());

context.echo(context);//订单流程校验


//此处的if-else逻辑只是为了表达状态的转换过程,并非实际的业务逻辑

if(context.isResultFlag()){// 订单校验成功 进入预返奖状态

context.setState(newBeforeRewardCheckState());

context.echo(context);

}else{// 订单校验失败 进入返奖失败状态

context.setState(newRewardFailState());

context.echo(context);

returnfalse;

       }

if(context.isResultFlag()){// 预返奖检查成功 进入返奖状态

context.setState(newSendRewardCheckState());

context.echo(context);

}else{// 预返奖检查失败 进入返奖失败状态

context.setState(newRewardFailState());

context.echo(context);

returnfalse;

       }

if(context.isResultFlag()){// 返奖成功 进入返奖成功状态

context.setState(newRewardSuccessState());

context.echo(context);

}else{// 返奖失败。进入补偿放奖状态

context.setState(newCompentstateRewardState());

context.echo(context);

       }

if(context.isResultFlag()){// 补偿返奖成功 进入成功状态

context.setState(newRewardSuccessState());

context.echo(context);

}else{// 补偿返奖失败 这里可以继续补偿返奖 可以认为控制补偿返奖次数。这里直接退出了

System.out.println("补偿反奖失败");

       }

returntrue;

   }

2.1.3 返奖流程与设计模式实践

你可能感兴趣的:(2020-03-23)