美团设计模式在外卖营销业务中的实践-学习笔记(一)
美团设计模式在外卖营销业务中的实践-学习笔记(一)一、设计模式原则二、设计模式在美团外卖营销业务中的具体案例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(Class
}
// 具体分工厂实现 (注意 这里知识命名为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(Class
}
// 具体的工厂 (根据具体的类生成不同的策略)
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 返奖流程与设计模式实践