背景在互联网业务开发中,我们经常会有这样的场景,比如一个事件,可以是红包事件,也可以是优惠券事件,也可以是秒杀活动等,要求该活动只能在特定场景下使用。例子:比如京东有很多优惠券,有些优惠券只能在京东使用,有些优惠券只能在京东7Fresh使用,有些优惠券只能在京东和京东使用.com7Fresh。我们马上可以想到一个解决方案,就是增加一个活动类型字段activityType,通过这个字段来区分这些活动。那么如何统一、高效、可扩展地存储这个活动ID,让你可以通过这个ID来判断这个活动在某些场景下是否可以使用呢?方案一可以通过枚举来实现,每个枚举包含2个属性——logo和scene,通过它来判断activity是否可以在场景中使用。1、比如存储标识只能在京东上使用为1,只能在淘宝特价版上使用标识为3,以此类推。packagecom.example.activitytype.constants;/***活动类型枚举**@authorhongcunlin*/publicenumActivityType{/***京东*/JD(1,"京东"),/***京东极速版*/JDJSB(2,"京东极速版"),/***京东极速版*/JD_JDJSB(3,"京东,京东极速版");/***Logo*/public整数代码;/***场景*/publicStringdesc;/***枚举**@paramcodeidentifier*@paramdescScene*/ActivityType(Integercode,Stringdesc){this.code=code;这个.desc=desc;}}在枚举中,列出了几个对应于活动类型的标识符。当然,标识符的数据类型也可以是字符串,这里简单的用整数来实现。2、以上判断使用为依据,仅对以下服务提供数据支持。以下服务是我们需要直接面对的业务。比如判断这个activity是否可以在京东使用,我们的实现方案对应为:packagecom.example.activitytype.service.impl;importcom.example.activitytype.constants.ActivityType;importcom.example.activitytype.service.ActivityTypeService;importorg.springframework.stereotype.Service;/***ActivityService**@authorhongcunlin*/@ServicepublicclassActivityTypeServiceImplimplementsActivityTypeService{/***是否可以在京东快捷版中使用**@参数代码标志*@returntrueyes/falseno*/@OverridepublicbooleanisCanUseInJdjsb(Integercode){returnActivityType.JDJSB.code.equals(code)||ActivityType.JD_JDJSB.code.equals(code);//TODO这里后续需要持续维护}}3.代码测试测试结果和我们预期的一样api.Test;导入org.springframework.boot.test.context.SpringBootTest;导入javax.annotation.Resource;@SpringBootTestclassActivityTypeTest{/***活动服务*/@ResourceprivateActivityTypeServiceactivityTypeService;/***是否可以在京东速递版使用*/@TestvoidisCanUseInJdjsbTest(){//1、京东,falseSystem.out.println(activityTypeService.isCanUseInJdjsb(1));//2、京东极速版,trueSystem.out.println(activityTypeService.isCanUseInJdjsb(2));//3、京东,京东极速版,trueSystem.out.println(activityTypeService.isCanUseInJdjsb(3));}}4。评估这个方案的好处是前期实施起来非常简单。缺点是随着维护的Activity类型越来越多,我们需要加入很多判断语句,而且每增加一个新的Activity类型,我们开发人员是完全不能接受的,把涉及到的方法都改一遍。方案二我们可以用二进制字符串01来存储这些场景,0表示不能使用,1表示可以使用。那么我们通过判断二进制字符串某个位置的值是否为1.used就可以很容易的判断是否可以在这个场景中使用。1.存储位置我们的枚举从通用标识符改为二进制存储位置。packagecom.example.activitytype.constants;/***活动类型枚举**@authorhongcunlin*/publicenumActivityIndex{/***001*京东*/JD(1,"京东"),/***010*京东极速版*/JDJSB(2,"京东极速版"),/***100*京东七鲜*/JD7F(4,"7Fresh");/***位置*/publicIntegerindex;/***场景*/publicStringdesc;/***枚举**@paramindex位置*@paramdesc场景*/ActivityIndex(Integerindex,Stringdesc){this.index=index;这个.desc=desc;}}如下图,我们将只能在京东极速版中使用的标识存储为2(二进制为010)。通过判断第二位是否为1,可以判断是否只能在京东快递版使用。2.判断是否使用包com.example.activitytype.service.impl;importcom.example.activitytype.constants.ActivityIndex;importcom.example.activitytype.service.ActivityIndexService;importorg.springframework.stereotype.Service;/***ActivityService**@authorhongcunlin*/@ServicepublicclassActivityIndexServiceImplimplementsActivityIndexService{/***是否可以在京东极速版中使用**@paramcodeidentifier*@returntrue可以/false不能*/@OverridepublicbooleanisCanUseInJdjsb(Integercode){//使用位运算判断值的二进制指定位是否为1return(code&ActivityIndex.JDJSB.index)!=0;}}这里采用位运算,简单高效。3、代码测试代码结果如预期packagecom.example.activitytype;importcom.example.activitytype.service.ActivityIndexService;importorg.junit.jupiter.api.Test;importorg.springframework.boot。test.context.SpringBootTest;importjavax.annotation.Resource;@SpringBootTestclassActivityIndexTest{/***活动服务*/@ResourceprivateActivityIndexServiceactivityIndexService;/***是否可以在京东快递版使用*/@TestvoidisCanUseInJdjsbTest(){//010,trueSystem.out.println(activityIndexService.isCanUseInJdjsb(2));//111,真System.out.println(activityIndexService.isCanUseInJdjsb(7));//100,falseSystem.out.println(activityIndexService.isCanUseInJdjsb(4));//001,错误System.out.println(activityIndexService.isCanUseInJdjsb(1));}}4.评估一下我们的枚举存储,不像方案一中的冗余特性,比如JDJSB和JD_JDJSB与JDJSB相交,这不符合编程中的OOP思想。另外我们通过位运算来判断,速度越快,也就是性能越好。最后,我们的代码会随着activity类型的增加而添加,无需开发,这意味着更好的可维护性。最后,本文的案例代码已经上传到github,有需要的同学可以下载https://github.com/larger5/activity-type-in??dex
