目录
前言
采用简单的注解方式进行业务策略模式
场景举例
实现方案
基本代码准备
基本功能接口定义
定义注解与不同的策略实现
业务实际使用
测试及结果展示
?
采用组合的注解方式进行业务策略模式
场景举例
订单来源pc端支付,然后不同支付方式和会员等级有不同策略
?订单来源手机端支付,然后不同支付方式和会员等级有不同策略
采用复杂的注解方式进行业务策略模式
场景举例
前言
平时的开发中往往需要嵌套的策略去解决一定的业务或底层问题,如果在上一层已经通过工厂模式和策略模式的综合使用_xiaofeng10330111的博客-CSDN博客该方式进行了相关的策略模式的使用,但是在紧接着的下层逻辑上再次使用相同逻辑的策略往往会创建大量的工厂去实现,各策略也需要向工厂写入内容,这个时候我往往会使用注解方式去实现对应的下层策略模式,主要采用方式写三个业务场景来使用展示,以便供大家记录,写的不对的可留言指正。
采用简单的注解方式进行业务策略模式
场景举例
假设有业务场景,前期通过整体的一些配置方案在线上共用的针对现有业务上做了通用配置,例如在商家售卖的商品具有资质以及业务管控,即配置一些商店(单店、总店、品牌门店等)可售卖的商品类目有指定的配置,在指定配置下进行具体商品的售卖,原来线上的配置在业务发展过程中发现有些门店的有些售卖类目可以进行个性化管控(跨境门店可单独售卖一些指定的类目,这些类目符合国家要求)但对原配置不产生影响,我们配置了针对特定门店或品牌等可以进行有自己的个性化业务个性化配置,现要求个性化业务上通过配置对指定门店有不同的策略。假设其他上层策略我们已经实现,就单独个性化配置的控制规则上通过注解实现控制规则的策略实现。
实现方案
基本代码准备
业务上个性换配置的主要信息如下
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.Builder;
import lombok.Data;
import java.util.List;
/**
* @author yanfengzhang
* @description 业务上个性化配置的规则
* @date 2022/3/5 00:33
*/
@Data
@Builder
public class OverrangeBusinessScope {
/**
* 唯一标识
*/
private long id;
/**
* 名称展示
*/
private String name;
/**
* 类型:品牌或门店等,例如:1-品牌;2-门店
*/
private int type;
/**
* 针对类型对应具体的影响范围
* 例如类型为门店,则对应对应需要干预的门店ID:110、112、112、113等
*/
private String effectRanges;
/**
* 控制规则:对原有配置的干预情况
*/
private int controlRule;
/**
* 校验规则:对原有校验结果进行干预
*/
private int validateRule;
/**
* 本次实际配置的类目信息
*/
private List<Long> relationCates;
/**
* 创建时间
*/
private long ctime;
/**
* 更新时间
*/
private long utime;
/**
* 创建人
*/
private String cname;
/**
* 修改人
*/
private String uname;
}
其中控制规则的常量选择如下:(只进行控制规则的,其他规则不做分析)
package org.zyf.javabasic.designpatterns.strategy.base;
/**
* @author yanfengzhang
* @description 超范围相关常量集合
* @date 2022/3/5 00:35
*/
public class OverrangeContants {
/**
* 控制规则
*/
public static class BizScopeControlRule {
/**
* 仅限指定类目
*/
public static final int ONLY = 1;
/**
* 需包含指定类目
*/
public static final int NEED = 2;
/**
* 不得包含指定类目
*/
public static final int NO = 3;
}
}
基本功能接口定义
按照不同的控制规则返回实际个性化门店或品牌的实际可以售卖的类目情况,
package org.zyf.javabasic.designpatterns.strategy.base;
import java.util.Collection;
/**
* @author yanfengzhang
* @description 规则控制基本实现接口定义
* @date 2022/3/5 00:37
*/
public interface ControlRuleHandler {
/**
* 根据当前配置类目信息进行分析处理
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return 最后可售卖的类目信息
* @throws Exception 业务异常
*/
Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception;
}
其中需要入参为超范围经营类目处理整合信息CagegoryDealInfo,需要传入当前平台已配置的通用化可售卖类目情况以及对应业务个性化配置的干预类目信息,具体内容如下:
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.Builder;
import lombok.Data;
import java.util.Collection;
/**
* @author yanfengzhang
* @description 超范围经营类目处理整合信息
* @date 2022/3/3 00:36
*/
@Data
@Builder
public class CagegoryDealInfo {
/**
* 平台当前配置的后台类目信息
*/
private Collection<Long> bgCategoryIds;
/**
* 指定业务范围配置的后台类目信息
*/
private Collection<Long> bgCategoryIdsForBizScope;
}
定义注解与不同的策略实现
基本注解定义
package org.zyf.javabasic.designpatterns.strategy.base;
import org.springframework.stereotype.Service;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yanfengzhang
* @description 基本控制规则注解
* @date 2022/3/5 00:38
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface ControlRuleHandlerType {
int controlRuleType();
}
策略1:控制规则,要求只返回个性化指定类目作为可售卖类目
package org.zyf.javabasic.designpatterns.strategy.base;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
/**
* @author yanfengzhang
* @description 只返回指定类目信息
* @date 2022/3/5 00:40
*/
@ControlRuleHandlerType(controlRuleType = OverrangeContants.BizScopeControlRule.ONLY)
@Service
public class ControlRuleForDesignatedHandler implements ControlRuleHandler {
@Autowired
private ProductCategoryService productCategoryService;
/**
* 只返回指定类目信息
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return
*/
@Override
public Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception {
return productCategoryService.fetchAllIdPathByCategoryId(cagegoryDealInfo.getBgCategoryIdsForBizScope());
}
}
策略2:控制规则,要求在原配置上增加个性化指定的一些类目,整体作为可售卖类目的情况
package org.zyf.javabasic.designpatterns.strategy.base;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author yanfengzhang
* @description 返回需包含指定类目信息
* @date 2022/3/5 00:42
*/
@ControlRuleHandlerType(controlRuleType = OverrangeContants.BizScopeControlRule.NEED)
@Service
public class ControlRuleForIncludeHandler implements ControlRuleHandler {
@Autowired
private ProductCategoryService productCategoryService;
/**
* 需要在原类目基础上增加指定类目信息
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return
*/
@Override
public Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception {
Collection<Long> bgCategoryIds = cagegoryDealInfo.getBgCategoryIds();
Collection<Long> bgCategoryIdsForBizScope = cagegoryDealInfo.getBgCategoryIdsForBizScope();
/*若两边没有配置返回为空*/
if (CollectionUtils.isEmpty(bgCategoryIds) && CollectionUtils.isEmpty(bgCategoryIdsForBizScope)) {
return bgCategoryIds;
}
/*将信息整合全部返回*/
Collection<Long> finalCategoryIds = new java.util.ArrayList<Long>(Collections.EMPTY_SET);
/*业务上找到联级关系全部的*/
Set<Long> allCategorysForBizScope = new HashSet<>(productCategoryService.fetchAllIdPathByCategoryId(bgCategoryIdsForBizScope));
finalCategoryIds.addAll(bgCategoryIds);
finalCategoryIds.addAll(allCategorysForBizScope);
return finalCategoryIds;
}
}
策略3:控制规则,要求在原配置上去除个性化指定的一些类目,然后整体作为可售卖类目的情况
package org.zyf.javabasic.designpatterns.strategy.base;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author yanfengzhang
* @description 返回不得包含指定类目信息
* @date 2022/3/5 00:45
*/
@ControlRuleHandlerType(controlRuleType = OverrangeContants.BizScopeControlRule.NO)
@Service
public class ControlRuleForNotContainHandler implements ControlRuleHandler {
@Autowired
private ProductCategoryService productCategoryService;
/**
* 需要在原类目基础上去掉不得包含指定类目信息
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return
*/
@Override
public Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception {
Collection<Long> bgCategoryIds = cagegoryDealInfo.getBgCategoryIds();
Collection<Long> bgCategoryIdsForBizScope = cagegoryDealInfo.getBgCategoryIdsForBizScope();
/*若两边没有配置返回为空*/
if (CollectionUtils.isEmpty(bgCategoryIds) && CollectionUtils.isEmpty(bgCategoryIdsForBizScope)) {
return bgCategoryIds;
}
if (CollectionUtils.isEmpty(bgCategoryIds)) {
return bgCategoryIds;
}
/*业务上找到联级关系全部的*/
Set<Long> allCategorysForBizScope = new HashSet<>(productCategoryService.fetchAllIdPathByCategoryId(bgCategoryIdsForBizScope));
return bgCategoryIds.stream().filter(bgCategoryId -> !allCategorysForBizScope.contains(bgCategoryId)).collect(Collectors.toList());
}
}
其中设置相关类目级别设置的可能是级层比较低,通过其他service进行补齐,本次不做复杂处理,只简单的为了测试随意写了一个,与实际处理无关,只为了测试。
package org.zyf.javabasic.designpatterns.strategy.base;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
* @author yanfengzhang
* @description
* @date 2022/3/5 00:40
*/
@Service
public class ProductCategoryService {
/**
* 根据当前的类目信息获取对应全链路路径信息 (做一个简单模拟)
*
* @param categoryIds 当前类目信息
* @return
*/
public Collection<Long> fetchAllIdPathByCategoryId(Collection<Long> categoryIds) {
Collection<Long> allIdPath = new ArrayList<Long>(Collections.EMPTY_SET);
if (CollectionUtils.isEmpty(categoryIds)) {
return allIdPath;
}
categoryIds.forEach(categoryId -> {
if (categoryId == 1) {
allIdPath.add(1L);
return;
}
if (categoryId == 2) {
allIdPath.add(2L);
return;
}
if (categoryId == 3) {
allIdPath.add(3L);
return;
}
if (categoryId == 4) {
allIdPath.add(4L);
return;
}
if (categoryId == 5) {
allIdPath.add(5L);
}
});
return allIdPath;
}
}
业务实际使用
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author yanfengzhang
* @description
* @date 2022/3/5 00:55
*/
@Service
@Log4j2
public class BizService {
private Map<Integer, ControlRuleHandler> brandControlRuleHandleMap;
/**
* 启动初始化各种类型的品牌控制规则处理类(通过ControlRuleHandlerType注解)
*
* @param brandControlRuleHandlers
*/
@Autowired
public void setBrandControlRuleHandleMap(List<ControlRuleHandler> brandControlRuleHandlers) {
// 注入各种类型的品牌控制规则处理类
brandControlRuleHandleMap = brandControlRuleHandlers.stream().collect(
Collectors.toMap(orderHandler -> AnnotationUtils.findAnnotation(orderHandler.getClass(), ControlRuleHandlerType.class).controlRuleType(),
v -> v, (v1, v2) -> v1));
}
/**
* 根据控制规则获取实际类目配置
*
* @param overrangeBusinessScope 个性化干预配置
* @param bgCategoryIds 实际现存配置
* @return 最终结果
* @throws Exception 业务异常
*/
public Collection<Long> getCagegoryIdsByControlRule(OverrangeBusinessScope overrangeBusinessScope, Collection<Long> bgCategoryIds) throws Exception {
if (null == overrangeBusinessScope && CollectionUtils.isEmpty(overrangeBusinessScope.getRelationCates())) {
return Lists.newArrayList();
}
/*1.获取指定控制范围内容*/
ControlRuleHandler controlRuleHandler = brandControlRuleHandleMap.get(overrangeBusinessScope.getControlRule());
/*2.分析对应指定类目信息与原类目信息进行处理*/
Collection<Long> bgCategoryIdsForBrand = overrangeBusinessScope.getRelationCates();
CagegoryDealInfo cagegoryDealInfo = CagegoryDealInfo.builder()
.bgCategoryIds(bgCategoryIds)
.bgCategoryIdsForBizScope(bgCategoryIdsForBrand)
.build();
return controlRuleHandler.getCagegoryIds(cagegoryDealInfo);
}
}
测试及结果展示
基本测试用例如下
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @author yanfengzhang
* @description
* @date 2022/3/5 00:59
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BaseStrategyTest {
@Autowired
private BizService bizService;
@Test
public void testBizFunction() throws Exception {
log.info("业务场景1---业务原配置类目内容为(1,2,3),现在个性化处理配置需要在原类目基础上增加以下类目(4,5)");
Long[] bgCategoryIdInfo1 = {1L,2L,3L};
Collection<Long> bgCategoryIds1= Arrays.asList(bgCategoryIdInfo1);
Long[] relationCateInfo1 = {4L,5L};
List<Long> relationCates1 = Arrays.asList(relationCateInfo1);
OverrangeBusinessScope overrangeBusinessScope1 = OverrangeBusinessScope.builder()
.controlRule(OverrangeContants.BizScopeControlRule.NEED)
.relationCates(relationCates1).build();
log.info("业务场景1情况下最终得到的业务类目为:{}",bizService.getCagegoryIdsByControlRule(overrangeBusinessScope1,bgCategoryIds1));
log.info("业务场景2---业务原配置类目内容为(1,2,3),现在个性化处理配置需要在原类目基础上不能包含以下类目(1,2)");
Long[] bgCategoryIdInfo2 = {1L,2L,3L};
Collection<Long> bgCategoryIds2= Arrays.asList(bgCategoryIdInfo2);
Long[] relationCateInfo2 = {1L,2L};
List<Long> relationCates2 = Arrays.asList(relationCateInfo2);
OverrangeBusinessScope overrangeBusinessScope2 = OverrangeBusinessScope.builder()
.controlRule(OverrangeContants.BizScopeControlRule.NO)
.relationCates(relationCates2).build();
log.info("业务场景2情况下最终得到的业务类目为:{}",bizService.getCagegoryIdsByControlRule(overrangeBusinessScope2,bgCategoryIds2));
log.info("业务场景3---业务原配置类目内容为(1,2,3, 4, 5),现在个性化处理配置需要不在意原有配置,当前只能售卖类目为(2,5)");
Long[] bgCategoryIdInfo3 = {1L,2L,3L,4L,5L};
Collection<Long> bgCategoryIds3= Arrays.asList(bgCategoryIdInfo3);
Long[] relationCateInfo3 = {2L,5L};
List<Long> relationCates3 = Arrays.asList(relationCateInfo3);
OverrangeBusinessScope overrangeBusinessScope3 = OverrangeBusinessScope.builder()
.controlRule(OverrangeContants.BizScopeControlRule.ONLY)
.relationCates(relationCates3).build();
log.info("业务场景3情况下最终得到的业务类目为:{}",bizService.getCagegoryIdsByControlRule(overrangeBusinessScope3,bgCategoryIds3));
}
}
测试结果展示
采用组合的注解方式进行业务策略模式
场景举例
假设我们在某个平台购物的时候,在订单生成后,我们对订单价格营销手段很简单,就是根据订单的来源(手机端或pc端)、支付方式(微信、支付宝或本地钱包)、对应用户的会员等级(新晋会员、白银会员、黄金会员、钻石会员等)等的不同会有不同的策略干预用户可享受的权益,具体如下:
订单来源pc端支付,然后不同支付方式和会员等级有不同策略
?订单来源手机端支付,然后不同支付方式和会员等级有不同策略
?此时按照注解方式实现相关的策略。以上场景只是为了我后续写代码方便,其实未必有该场景。
相关代码用空我在写。。。。。
采用复杂的注解方式进行业务策略模式
场景举例
假设我们在某个平台购物的时候,在订单生成后,我们对订单价格营销手段很简单,就是根据订单的来源(手机端或pc端)、支付方式(微信、支付宝或本地钱包)、对应用户的会员等级(新晋会员、白银会员、黄金会员、钻石会员等)等的不同来干预用户可享受的权益,但是有的组合方式享受的权益内容是一样的,具体如下:
订单来源pc端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)
?订单来源手机端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)
此时按照注解方式实现相关的策略,要求别写重复策略代码(因为我知道可以按照上面的组合方式改注解标记来实现,然后copy代码就可以)。以上场景只是为了我后续写代码方便,其实未必有该场景。
相关代码用空我在写。。。。。
|