采用注解实现策略模式

article/2025/10/4 12:48:47

目录

一、前言

二、采用简单的注解方式进行业务策略模式

(一)场景举例

(二)实现方案

1、基本代码准备

2、基本功能接口定义

3、定义注解与不同的策略实现

4、业务实际使用

5、测试及结果展示

三、采用组合的注解方式进行业务策略模式

(一)场景举例

订单来源pc端支付,然后不同支付方式和会员等级有不同策略

 订单来源手机端支付,然后不同支付方式和会员等级有不同策略

(二)实现方案

1、基本代码准备

2、基本功能接口定义

3、定义注解与实现

4、各策略实现举例

5、业务实际使用

6、测试及结果展示

四、采用复杂的注解方式进行业务策略模式

(一)场景举例

订单来源pc端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)

 订单来源手机端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)

(二)实现方案

1、基本代码准备

2、基本功能接口定义

3、定义注解

4、不同策略的实现举例

5、业务实际应用

6、测试及结果展示


一、前言

平时的开发中往往需要嵌套的策略去解决一定的业务或底层问题,如果在上一层已经通过工厂模式和策略模式的综合使用_xiaofeng10330111的博客-CSDN博客该方式进行了相关的策略模式的使用,但是在紧接着的下层逻辑上再次使用相同逻辑的策略往往会创建大量的工厂去实现,各策略也需要向工厂写入内容,这个时候我往往会使用注解方式去实现对应的下层策略模式,主要采用方式写三个业务场景来使用展示,以便供大家记录,写的不对的可留言指正

二、采用简单的注解方式进行业务策略模式

(一)场景举例

假设有业务场景,前期针对现有业务整体上做了通用性配置(例如商家售卖的商品需要符合特定相关资质和业务管控,即:商家是药店,只能售卖一些符合要求的指定药物相关类目,同时具体买那些药物或医疗器械,该商家必须有对应的资质才可以),在指定配置下进行具体商品的售卖,原来线上的配置在业务发展过程中发现有些门店的有些售卖类目可以进行个性化管控(跨境门店可单独售卖一些指定的类目,这些类目符合国家要求)但对原配置不产生影响,我们配置了针对特定门店或品牌等可以进行有自己的个性化业务个性化配置,现要求个性化业务上通过配置对指定门店有不同的策略。假设其他上层策略我们已经实现,就单独个性化配置的控制规则上通过注解实现控制规则的策略实现。

(二)实现方案

1、基本代码准备

业务上个性换配置的主要信息如下

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;}
}

2、基本功能接口定义

按照不同的控制规则返回实际个性化门店或品牌的实际可以售卖的类目情况,

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;
}

3、定义注解与不同的策略实现

基本注解定义

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 {@Autowiredprivate ProductCategoryService productCategoryService;/*** 只返回指定类目信息** @param cagegoryDealInfo 需要的处理的类目信息* @return*/@Overridepublic 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 {@Autowiredprivate ProductCategoryService productCategoryService;/*** 需要在原类目基础上增加指定类目信息** @param cagegoryDealInfo 需要的处理的类目信息* @return*/@Overridepublic 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 {@Autowiredprivate ProductCategoryService productCategoryService;/*** 需要在原类目基础上去掉不得包含指定类目信息** @param cagegoryDealInfo 需要的处理的类目信息* @return*/@Overridepublic 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;}
}

4、业务实际使用

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*/@Autowiredpublic 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);}
}

5、测试及结果展示

基本测试用例如下

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 {@Autowiredprivate BizService bizService;@Testpublic 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端支付,然后不同支付方式和会员等级有不同策略

 订单来源手机端支付,然后不同支付方式和会员等级有不同策略

 此时按照注解方式实现相关的策略。以上场景只是为了我后续写代码方便,其实未必有该场景。

(二)实现方案

1、基本代码准备

我们前期先定义一个简单的基本消费订单类Order,基本内容主要包含以下一些基本订单信息:

package org.zyf.javabasic.designpatterns.strategy.combination;import lombok.Data;import java.math.BigDecimal;/*** @author yanfengzhang* @description 订单实体类* @date 2022/3/8  23:10*/
@Data
public class Order {/*** 订单编号*/private String code;/*** 订单金额*/private BigDecimal amount;/*** 订单来源:手机端、pc端*/private OrderSourceEnum source;/*** 支付方式:微信支付、支付宝支付、银行卡支付、本地钱包支付等*/private OrderPayMethodEnum payMethod;/*** 订单支付会员归类:*/private MemberTypeEnum payMember;
}

同时,我们在定义一个实际消费完订单的结果类OrderConsumerResult,基本信息如下:

package org.zyf.javabasic.designpatterns.strategy.combination;import lombok.Builder;
import lombok.Data;import java.math.BigDecimal;/*** @author yanfengzhang* @description 订单消费结果* @date 2022/3/8  23:14*/
@Data
@Builder
public class OrderConsumerResult {/*** 订单编号*/private String code;/*** 实际支付金额*/private BigDecimal amountSpent;/*** 本次订单可获取的权益情况 (只做简单测试,所以不做复杂逻辑处理了)*/private String right;
}

为了方便注解,相关基本信息我们使用枚举进行处理,相关枚举如下定义:

订单来源枚举

package org.zyf.javabasic.designpatterns.strategy.combination;/*** @author yanfengzhang* @description 订单来源* @date 2022/3/8  23:16*/
public enum OrderSourceEnum {PC(0, "pc端订单"),MOBILE(1, "手机端订单");/*** 订单来源方*/private Integer source;/*** 描述*/private String desc;public Integer getsource() {return source;}public String getDesc() {return desc;}OrderSourceEnum(Integer source) {this.source = source;}OrderSourceEnum(Integer source, String desc) {this.desc = desc;this.source = source;}/*** 根据订单来源类型source获取对应的订单来源类型枚举** @param source 订单来源类型source* @return*/public static OrderSourceEnum getEnumBySource(Integer source) {for (OrderSourceEnum orderSourceEnum : OrderSourceEnum.values()) {if (orderSourceEnum.getsource().equals(source)) {return orderSourceEnum;}}return null;}/*** 根据订单来源类型描述获取对应的订单来源类型枚举** @param desc 订单来源类型描述* @return*/public static OrderSourceEnum getEnumByDesc(String desc) {for (OrderSourceEnum orderSourceEnum : OrderSourceEnum.values()) {if (orderSourceEnum.getDesc().equals(desc)) {return orderSourceEnum;}}return null;}/*** 判断类型是否为指定类型** @param source 订单来源类型source* @return*/public static boolean isAppealTypeEnum(Integer source) {if (source == null) {return false;}for (OrderSourceEnum tempEnum : OrderSourceEnum.values()) {if (tempEnum.getsource().equals(source)) {return true;}}return false;}
}

订单支付方式枚举

package org.zyf.javabasic.designpatterns.strategy.combination;/*** @author yanfengzhang* @description 订单支付方式枚举* @date 2022/3/8  23:19*/
public enum OrderPayMethodEnum {WEIXIN(0, "微信支付"),ZHIFUBAO(1, "支付宝支付"),WALLET(2, "本地钱包支付");/*** 订单支付方式*/private Integer payType;/*** 描述*/private String desc;public Integer getPayType() {return payType;}public String getDesc() {return desc;}OrderPayMethodEnum(Integer payType) {this.payType = payType;}OrderPayMethodEnum(Integer payType, String desc) {this.desc = desc;this.payType = payType;}/*** 根据订单支付类型payType获取对应的订单支付类型枚举** @param payType 订单支付类型payType* @return*/public static OrderPayMethodEnum getEnumBypayType(Integer payType) {for (OrderPayMethodEnum orderPayMethodEnum : OrderPayMethodEnum.values()) {if (orderPayMethodEnum.getPayType().equals(payType)) {return orderPayMethodEnum;}}return null;}/*** 根据订单支付类型描述获取对应的订单支付类型枚举** @param desc 订单支付类型描述* @return*/public static OrderPayMethodEnum getEnumByDesc(String desc) {for (OrderPayMethodEnum orderPayMethodEnum : OrderPayMethodEnum.values()) {if (orderPayMethodEnum.getDesc().equals(desc)) {return orderPayMethodEnum;}}return null;}/*** 判断类型是否为指定类型** @param payType 订单支付类型payType* @return*/public static boolean isAppealTypeEnum(Integer payType) {if (payType == null) {return false;}for (OrderPayMethodEnum tempEnum : OrderPayMethodEnum.values()) {if (tempEnum.getPayType().equals(payType)) {return true;}}return false;}
}

订单会员等级枚举

package org.zyf.javabasic.designpatterns.strategy.combination;/*** @author yanfengzhang* @description 订单会员类型* @date 2022/3/8  23:22*/
public enum MemberTypeEnum {NONACTIVATED(0, "非会员,未激活"),NEWCOMER(1, "新晋会员"),SILVER(2, "白银会员"),GOLD(3, "黄金会员"),PLATINUM(4, "铂金会员");/*** 订单会员类型*/private Integer type;/*** 描述*/private String desc;public Integer getType() {return type;}public String getDesc() {return desc;}MemberTypeEnum(Integer type) {this.type = type;}MemberTypeEnum(Integer type, String desc) {this.desc = desc;this.type = type;}/*** 根据订单会员类型type获取对应的订单会员类型枚举** @param type 订单会员类型type* @return*/public static MemberTypeEnum getEnumBytype(Integer type) {for (MemberTypeEnum memberTypeEnum : MemberTypeEnum.values()) {if (memberTypeEnum.getType().equals(type)) {return memberTypeEnum;}}return null;}/*** 根据订单会员类型描述获取对应的订单会员类型枚举** @param desc 订单会员类型描述* @return*/public static MemberTypeEnum getEnumByDesc(String desc) {for (MemberTypeEnum memberTypeEnum : MemberTypeEnum.values()) {if (memberTypeEnum.getDesc().equals(desc)) {return memberTypeEnum;}}return null;}/*** 判断类型是否为指定类型** @param type 订单会员类型type* @return*/public static boolean isAppealTypeEnum(Integer type) {if (type == null) {return false;}for (MemberTypeEnum tempEnum : MemberTypeEnum.values()) {if (tempEnum.getType().equals(type)) {return true;}}return false;}
}

2、基本功能接口定义

我们根据当前订单的基本信息来决定最终的消费结果,具体结果定义简单如下:

package org.zyf.javabasic.designpatterns.strategy.combination;/*** @author yanfengzhang* @description 规定处理订单的方法* @date 2022/3/8  23:25*/
public interface OrderHandler {/*** 处理订单生成消费结果** @param order 订单基本信息* @return 订单实际消费结果*/OrderConsumerResult handle(Order order);
}

3、定义注解与实现

按照当前的基本策略处理可以基本定义注解如下:

package org.zyf.javabasic.designpatterns.strategy.combination;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/8  23:27*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface OrderHandlerType {/*** 指明来源*/OrderSourceEnum source();/*** 支付方式*/OrderPayMethodEnum payMethod();/*** 会员类型*/MemberTypeEnum memberType();
}

注解本身为接口的语法糖,所以为了组合有效,我们必要实现对应的基本逻辑和相关的hashcode和equals方法等,先简单处理如下展示:

package org.zyf.javabasic.designpatterns.strategy.combination;import java.lang.annotation.Annotation;/*** @author yanfengzhang* @description 实现注解语法糖的实现类* @date 2022/3/8  23:33*/
public class OrderHandlerTypeImpl implements OrderHandlerType {private OrderSourceEnum source;private OrderPayMethodEnum payMethod;private MemberTypeEnum memberType;OrderHandlerTypeImpl(OrderSourceEnum source, OrderPayMethodEnum payMethod, MemberTypeEnum memberType) {this.source = source;this.payMethod = payMethod;this.memberType = memberType;}@Overridepublic OrderSourceEnum source() {return source;}@Overridepublic OrderPayMethodEnum payMethod() {return payMethod;}@Overridepublic MemberTypeEnum memberType() {return memberType;}@Overridepublic Class<? extends Annotation> annotationType() {return OrderHandlerType.class;}@Overridepublic int hashCode() {int hashCode = 0;hashCode += (127 * "source".hashCode()) ^ source.hashCode();hashCode += (127 * "payMethod".hashCode()) ^ payMethod.hashCode();hashCode += (127 * "memberType".hashCode()) ^ memberType.hashCode();return hashCode;}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof OrderHandlerType)) {return false;}OrderHandlerType other = (OrderHandlerType) obj;return source == other.source()&& payMethod == other.payMethod()&& memberType == other.memberType();}
}

4、各策略实现举例

按原图上来说的话,每一个都需要写对应的策略实现类,由于只是为了我门基本功能的测试,我在这边暂时不会写那么多,只选几种,将其功能简单展示如下:

pc端微信支付,用户为未开通状态

package org.zyf.javabasic.designpatterns.strategy.combination;import lombok.extern.log4j.Log4j2;/*** @author yanfengzhang* @description pc端微信支付,用户为未开通状态* @date 2022/3/8  23:35*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.WEIXIN, memberType = MemberTypeEnum.NONACTIVATED)
public class PCWXNonactivatedOrderHandler implements OrderHandler {/*** 进行原价支付** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(order.getAmount()).right("该会员不参与其他权益履约适宜").build();}
}

pc端支付宝支付,用户当前为新晋会员

package org.zyf.javabasic.designpatterns.strategy.combination;import lombok.extern.log4j.Log4j2;import java.math.BigDecimal;/*** @author yanfengzhang* @description pc端支付宝支付,用户当前为新晋会员* @date 2022/3/8  23:37*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.ZHIFUBAO, memberType = MemberTypeEnum.NEWCOMER)
public class PCZFBNewcomerOrderHandler implements OrderHandler {/*** 支持满120减100优惠,同时赠送新晋套餐** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(120);BigDecimal discountedPrice = BigDecimal.valueOf(100);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("赠送新晋套餐").build();}
}

pc端本地钱包支付支付,用户为未开通状态

package org.zyf.javabasic.designpatterns.strategy.combination;/*** @author yanfengzhang* @description* @date 2022/3/8  23:38*/import lombok.extern.log4j.Log4j2;/*** @author yanfengzhang* @description pc端本地钱包支付支付,用户为未开通状态* @date 2022/3/8  23:39*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.NONACTIVATED)
public class PCWalletNonactivatedOrderHandler implements OrderHandler {/*** 存入钱包50得60,订单满30减30** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(order.getAmount()).right("存入钱包50得60,订单满30减30").build();}
}

手机端微信支付,用户为未开通状态

package org.zyf.javabasic.designpatterns.strategy.combination;import java.math.BigDecimal;/*** @author yanfengzhang* @description 手机端微信支付,用户为未开通状态* @date 2022/3/8  23:42*/
@OrderHandlerType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WEIXIN, memberType = MemberTypeEnum.NONACTIVATED)
public class MobileWXNonactivatedOrderHandler implements OrderHandler {/*** 订单满10减3,送腾讯会员3天** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(10);BigDecimal discountedPrice = BigDecimal.valueOf(3);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("送腾讯会员3天").build();}
}

手机端支付宝支付,用户当前为新晋会员

package org.zyf.javabasic.designpatterns.strategy.combination;import lombok.extern.log4j.Log4j2;import java.math.BigDecimal;/*** @author yanfengzhang* @description 手机端支付宝支付,用户当前为新晋会员* @date 2022/3/8  23:46*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.ZHIFUBAO, memberType = MemberTypeEnum.NEWCOMER)
public class MobileZFBNewcomerOrderHandler implements OrderHandler {/*** 支持满130减120优惠,同时赠送新晋套餐** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(130);BigDecimal discountedPrice = BigDecimal.valueOf(120);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("赠送新晋套餐").build();}
}

本地钱包支付宝支付,用户当前为黄金会员

package org.zyf.javabasic.designpatterns.strategy.combination;import lombok.extern.log4j.Log4j2;import java.math.BigDecimal;/*** @author yanfengzhang* @description 本地钱包支付宝支付,用户当前为黄金会员* @date 2022/3/8  23:48*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.GOLD)
public class MobileWalletGoldOrderHandler implements OrderHandler {/*** 存入钱包200得400,套餐支持满100减20优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(100);BigDecimal discountedPrice = BigDecimal.valueOf(20);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("存入钱包200得400,同时送减免整合套餐和内部优惠无限制满5减5(2张)").build();}
}

5、业务实际使用

我们写一个实际业务使用的简单例子,实现实际业务订单处理服务定义如下:

package org.zyf.javabasic.designpatterns.strategy.combination;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;/*** @author yanfengzhang* @description 实际业务订单处理服务* @date 2022/3/8  23:53*/
@Service
public class OrderService {private Map<OrderHandlerType, OrderHandler> orderHandleMap;@Autowiredpublic void setOrderHandleMap(List<OrderHandler> orderHandlers) {// 注入各种类型的订单处理类orderHandleMap = orderHandlers.stream().collect(Collectors.toMap(orderHandler -> AnnotationUtils.findAnnotation(orderHandler.getClass(), OrderHandlerType.class),v -> v, (v1, v2) -> v1));}/*** 只做测试,不进行前置校验或其他订单逻辑,主要就直接按策略看最后的结果** @param order 当前订单信息* @return 订单处理结果*/public OrderConsumerResult orderService(Order order) {// 通过订单来源、支付方式和会员等级获取对应的handlerOrderHandlerType orderHandlerType = new OrderHandlerTypeImpl(order.getSource(), order.getPayMethod(), order.getPayMember());OrderHandler orderHandler = orderHandleMap.get(orderHandlerType);if (Objects.isNull(orderHandler)) {/*没有匹配的策略直接原价处理并不参与权益处理*/return OrderConsumerResult.builder().code(order.getCode()).amountSpent(order.getAmount()).right("该会员不参与其他权益履约适宜").build();}return orderHandler.handle(order);}
}

6、测试及结果展示

基本测试如下,进行了六个场景的模拟:

package org.zyf.javabasic.designpatterns.strategy.combination;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.math.BigDecimal;
import java.util.concurrent.ExecutionException;/*** @author yanfengzhang* @description* @date 2022/3/8  23:58*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class OrderServiceTest {@Autowiredprivate OrderService orderService;@Testpublic void testCombination() throws ExecutionException {log.info("测试场景1:(订单编号-000001)用户未开通会员,在pc端通过微信支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:用户需进行原价支付");Order order1 = new Order();order1.setCode("000001");order1.setAmount(new BigDecimal("131.7"));order1.setSource(OrderSourceEnum.PC);order1.setPayMethod(OrderPayMethodEnum.WEIXIN);order1.setPayMember(MemberTypeEnum.NONACTIVATED);OrderConsumerResult orderConsumerResult1 = orderService.orderService(order1);log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult1.getCode(), orderConsumerResult1.getAmountSpent(), orderConsumerResult1.getRight());log.info("测试场景2:(订单编号-000002)用户为新晋会员,在pc端通过支付宝支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:支持满120减100优惠,同时赠送新晋套餐");Order order2 = new Order();order2.setCode("000002");order2.setAmount(new BigDecimal("131.7"));order2.setSource(OrderSourceEnum.PC);order2.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);order2.setPayMember(MemberTypeEnum.NEWCOMER);OrderConsumerResult orderConsumerResult2 = orderService.orderService(order2);log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult2.getCode(), orderConsumerResult2.getAmountSpent(), orderConsumerResult2.getRight());log.info("测试场景3:(订单编号-000003)用户未开通会员,在pc端通过本地钱包支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:存入钱包50得60,订单满30减30");Order order3 = new Order();order3.setCode("000003");order3.setAmount(new BigDecimal("131.7"));order3.setSource(OrderSourceEnum.PC);order3.setPayMethod(OrderPayMethodEnum.WALLET);order3.setPayMember(MemberTypeEnum.NONACTIVATED);OrderConsumerResult orderConsumerResult3 = orderService.orderService(order3);log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult3.getCode(), orderConsumerResult3.getAmountSpent(), orderConsumerResult3.getRight());log.info("测试场景4:(订单编号-000004)用户未开通会员,在手机端通过微信支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:订单满10减3,送腾讯会员3天");Order order4 = new Order();order4.setCode("000004");order4.setAmount(new BigDecimal("131.7"));order4.setSource(OrderSourceEnum.MOBILE);order4.setPayMethod(OrderPayMethodEnum.WEIXIN);order4.setPayMember(MemberTypeEnum.NONACTIVATED);OrderConsumerResult orderConsumerResult4 = orderService.orderService(order4);log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult4.getCode(), orderConsumerResult4.getAmountSpent(), orderConsumerResult4.getRight());log.info("测试场景5:(订单编号-000005)用户为新晋会员,在手机端通过支付宝支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:支持满130减120优惠,同时赠送新晋套餐");Order order5 = new Order();order5.setCode("000005");order5.setAmount(new BigDecimal("131.7"));order5.setSource(OrderSourceEnum.MOBILE);order5.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);order5.setPayMember(MemberTypeEnum.NEWCOMER);OrderConsumerResult orderConsumerResult5 = orderService.orderService(order5);log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult5.getCode(), orderConsumerResult5.getAmountSpent(), orderConsumerResult5.getRight());log.info("测试场景6:(订单编号-000006)用户为黄金会员,在手机端通过本地钱包支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:存入钱包200得400,套餐支持满100减20优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)");Order order6 = new Order();order6.setCode("000006");order6.setAmount(new BigDecimal("131.7"));order6.setSource(OrderSourceEnum.MOBILE);order6.setPayMethod(OrderPayMethodEnum.WALLET);order6.setPayMember(MemberTypeEnum.GOLD);OrderConsumerResult orderConsumerResult6 = orderService.orderService(order6);log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult6.getCode(), orderConsumerResult6.getAmountSpent(), orderConsumerResult6.getRight());}
}

最后基本测试结果如下展示

四、采用复杂的注解方式进行业务策略模式

(一)场景举例

假设我们在某个平台购物的时候,在订单生成后,我们对订单价格营销手段很简单,就是根据订单的来源(手机端或pc端)、支付方式(微信、支付宝或本地钱包)、对应用户的会员等级(新晋会员、白银会员、黄金会员、钻石会员等)等的不同来干预用户可享受的权益,但是有的组合方式享受的权益内容是一样的,具体如下:

订单来源pc端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)

 订单来源手机端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)

此时按照注解方式实现相关的策略,要求别写重复策略代码(因为我知道可以按照上面的组合方式改注解标记来实现,然后copy代码就可以)。以上场景只是为了我后续写代码方便,其实未必有该场景。

(二)实现方案

1、基本代码准备

本部分的基本代码内容和上面三-(二)-1的内容相一致,不在展示。

2、基本功能接口定义

本部分的基本代码内容和上面三-(二)-2的内容相一致,不在展示。

3、定义注解

基本上来看的话,我们当前的需求在支付方式上是有重叠的,所以我们可以将支付方式按数组方式进行定义,具体如下:

package org.zyf.javabasic.designpatterns.strategy.complex;import org.springframework.stereotype.Service;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;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/11  23:41*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface OrderProcessorType {/*** 指明来源*/OrderSourceEnum source();/*** 支付方式*/OrderPayMethodEnum[] payMethod();/*** 会员类型*/MemberTypeEnum memberType();
}

4、不同策略的实现举例

因为策略比较多,我们依旧选取几个进行展示即可:

pc端微信或支付宝支付,用户当前为新晋会员

package org.zyf.javabasic.designpatterns.strategy.complex;import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;import java.math.BigDecimal;/*** @author yanfengzhang* @description pc端微信或支付宝支付,用户当前为新晋会员* @date 2022/3/11  23:44*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.PC, payMethod = {OrderPayMethodEnum.ZHIFUBAO, OrderPayMethodEnum.WEIXIN}, memberType = MemberTypeEnum.NEWCOMER)
public class PCWZNewcomerOrderCpxHandler implements OrderHandler {/*** 支持满100减50优惠,同时赠送新晋套餐** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(100);BigDecimal discountedPrice = BigDecimal.valueOf(50);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("赠送新晋套餐").build();}
}

pc端支付宝支付,用户当前为黄金会员

package org.zyf.javabasic.designpatterns.strategy.complex;import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;import java.math.BigDecimal;/*** @author yanfengzhang* @description pc端支付宝支付,用户当前为黄金会员* @date 2022/3/11  23:46*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.ZHIFUBAO, memberType = MemberTypeEnum.GOLD)
public class PCZFBGoldOrderCpxHandler implements OrderHandler {/*** 套餐支持满100减40优惠,同时送减免整合套餐** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(100);BigDecimal discountedPrice = BigDecimal.valueOf(40);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("送减免整合套餐").build();}
}

pc端本地钱包支付,用户当前为白银会员

package org.zyf.javabasic.designpatterns.strategy.complex;import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;import java.math.BigDecimal;/*** @author yanfengzhang* @description pc端本地钱包支付,用户当前为白银会员* @date 2022/3/11  23:47*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.SILVER)
public class PCWalletSilverOrderCpxHandler implements OrderHandler {/*** 套餐支持满100减10优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(100);BigDecimal discountedPrice = BigDecimal.valueOf(10);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("送减免整合套餐和内部优惠无限制满5减5(2张)").build();}
}

手机端端微信或支付宝支付,用户当前未开通会员

package org.zyf.javabasic.designpatterns.strategy.complex;import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;import java.math.BigDecimal;/*** @author yanfengzhang* @description 手机端端微信或支付宝支付,用户当前未开通会员* @date 2022/3/11  23:50*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.MOBILE, payMethod = {OrderPayMethodEnum.ZHIFUBAO, OrderPayMethodEnum.WEIXIN}, memberType = MemberTypeEnum.NONACTIVATED)
public class MobileWZNonactivatedOrderCpxHandler implements OrderHandler {/*** 订单满10减3,送腾讯会员7天或爱奇艺会员7天(随机)** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {BigDecimal amount = order.getAmount();BigDecimal basePrice = BigDecimal.valueOf(10);BigDecimal discountedPrice = BigDecimal.valueOf(3);if (amount.compareTo(basePrice) < 0) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount).right("当前没有新增权益内容").build();}return OrderConsumerResult.builder().code(order.getCode()).amountSpent(amount.subtract(discountedPrice)).right("送腾讯会员7天或爱奇艺会员7天(随机)").build();}
}

手机端微信支付,用户当前为黄金会员

package org.zyf.javabasic.designpatterns.strategy.complex;import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;/*** @author yanfengzhang* @description 手机端微信支付,用户当前为黄金会员* @date 2022/3/11  23:52*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WEIXIN, memberType = MemberTypeEnum.GOLD)
public class MobileWXGoldOrderCpxHandler implements OrderHandler {/*** 套餐支持满160减140优惠、减110、减100各一张优惠,同时送特别礼品(随机)** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(order.getAmount()).right("本次套餐支持满160减140优惠、减110、减100各一张优惠,同时送特别礼品(随机),后续履约").build();}
}

手机端本地钱包支付,用户当前为铂金会员

package org.zyf.javabasic.designpatterns.strategy.complex;import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;/*** @author yanfengzhang* @description 手机端本地钱包支付,用户当前为铂金会员* @date 2022/3/11  23:55*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.PLATINUM)
public class MobileWalletPlatinumOrderCpxHandler implements OrderHandler {/*** 存入钱包500得800,套餐支持满100减50优惠、减40、减30各一张优惠,同时送特别礼品(随机)和内部优惠无限制满5减5(3张)** @param order 基本订单情况* @return 实际消费结果*/@Overridepublic OrderConsumerResult handle(Order order) {return OrderConsumerResult.builder().code(order.getCode()).amountSpent(order.getAmount()).right("存入钱包500得800,套餐支持满100减50优惠、减40、减30各一张优惠,同时送特别礼品(随机)和内部优惠无限制满5减5(3张),后续履约").build();}
}

5、业务实际应用

在业务实际调用上,需要去匹配对应的策略,所以我自己先定义分发存储processorRepository,其key为我们以上的相关组装,对应的value为我们实际的策略实现,以下我初步写的基本代码:

策略的基本组合key

package org.zyf.javabasic.designpatterns.strategy.complex;import lombok.Builder;
import lombok.Data;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;import java.util.Objects;/*** @author yanfengzhang* @description 策略的基本组合体* @date 2022/3/11  23:59*/
@Data
@Builder
public class StrategyInfo {/*** 指明来源*/OrderSourceEnum source;/*** 支付方式*/OrderPayMethodEnum payMethod;/*** 会员类型*/MemberTypeEnum memberType;@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (!(o instanceof StrategyInfo)) {return false;}StrategyInfo that = (StrategyInfo) o;return source == that.source &&payMethod == that.payMethod &&memberType == that.memberType;}@Overridepublic int hashCode() {return Objects.hash(source, payMethod, memberType);}
}

实际业务服务

package org.zyf.javabasic.designpatterns.strategy.complex;import com.google.common.collect.Maps;
import lombok.extern.log4j.Log4j2;
import org.reflections.Reflections;
import org.springframework.stereotype.Service;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.Objects;
import java.util.Set;/*** @author yanfengzhang* @description 实际业务订单处理服务* @date 2022/3/12  00:00*/
@Service
@Log4j2
public class OrderCpxService {/*** 策略表*/private static Map<StrategyInfo, Class> processorRepository = Maps.newHashMap();static {Reflections reflections = new Reflections("org.zyf.javabasic.designpatterns.strategy.complex");Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(OrderProcessorType.class);for (Class<?> cl : classSet) {Annotation[] annotations = cl.getAnnotations();if (annotations != null) {for (Annotation annotation : annotations) {if (annotation instanceof OrderProcessorType) {OrderProcessorType dispatch = (OrderProcessorType) annotation;OrderSourceEnum source = dispatch.source();OrderPayMethodEnum[] payMethods = dispatch.payMethod();MemberTypeEnum memberType = dispatch.memberType();for (OrderPayMethodEnum payMethod : payMethods) {processorRepository.put(StrategyInfo.builder().source(source).payMethod(payMethod).memberType(memberType).build(), cl);}}}}}}/*** 只做测试,不进行前置校验或其他订单逻辑,主要就直接按策略看最后的结果** @param order 当前订单信息* @return 订单处理结果*/public OrderConsumerResult orderService(Order order) throws Exception {StrategyInfo strategyInfo = StrategyInfo.builder().source(order.getSource()).payMethod(order.getPayMethod()).memberType(order.getPayMember()).build();try {Constructor constructor = processorRepository.get(strategyInfo).getConstructor();OrderHandler orderHandler = (OrderHandler) constructor.newInstance();if (Objects.isNull(orderHandler)) {/*没有匹配的策略直接原价处理并不参与权益处理*/return OrderConsumerResult.builder().code(order.getCode()).amountSpent(order.getAmount()).right("该会员不参与其他权益履约适宜").build();}return orderHandler.handle(order);} catch (Exception e) {log.error("OrderCpxService orderService error,order={}", order);throw new Exception("依旧当前订单信息处理营销信息失败!");}}
}

6、测试及结果展示

基本的测试代码

package org.zyf.javabasic.designpatterns.strategy.complex;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 org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;import java.math.BigDecimal;/*** @author yanfengzhang* @description* @date 2022/3/12  00:13*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class OrderCpxServiceTest {@Autowiredprivate OrderCpxService orderCpxService;@Testpublic void testComplex() throws Exception {log.info("测试场景1:(订单编号-000001)用户为新晋会员,在pc端通过微信或支付宝支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:支持满100减50优惠,同时赠送新晋套餐");Order order1 = new Order();order1.setCode("000001");order1.setAmount(new BigDecimal("131.7"));order1.setSource(OrderSourceEnum.PC);order1.setPayMethod(OrderPayMethodEnum.WEIXIN);order1.setPayMember(MemberTypeEnum.NEWCOMER);OrderConsumerResult orderConsumerResult11 = orderCpxService.orderService(order1);log.info("测试场景1结果:(在微信支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult11.getCode(), orderConsumerResult11.getAmountSpent(), orderConsumerResult11.getRight());order1.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);OrderConsumerResult orderConsumerResult12 = orderCpxService.orderService(order1);log.info("测试场景1结果:(在支付宝支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult12.getCode(), orderConsumerResult12.getAmountSpent(), orderConsumerResult12.getRight());log.info("测试场景2:(订单编号-000002)用户为黄金会员,在pc端通过支付宝支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:套餐支持满100减40优惠,同时送减免整合套餐");Order order2 = new Order();order2.setCode("000002");order2.setAmount(new BigDecimal("131.7"));order2.setSource(OrderSourceEnum.PC);order2.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);order2.setPayMember(MemberTypeEnum.GOLD);OrderConsumerResult orderConsumerResult2 = orderCpxService.orderService(order2);log.info("测试场景2结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult2.getCode(), orderConsumerResult2.getAmountSpent(), orderConsumerResult2.getRight());log.info("测试场景3:(订单编号-000003)用户为白银会员,在pc端通过本地钱包支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:套餐支持满100减10优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)");Order order3 = new Order();order3.setCode("000003");order3.setAmount(new BigDecimal("131.7"));order3.setSource(OrderSourceEnum.PC);order3.setPayMethod(OrderPayMethodEnum.WALLET);order3.setPayMember(MemberTypeEnum.SILVER);OrderConsumerResult orderConsumerResult3 = orderCpxService.orderService(order3);log.info("测试场景3结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult3.getCode(), orderConsumerResult3.getAmountSpent(), orderConsumerResult3.getRight());log.info("测试场景4:(订单编号-000004)用户未开通会员,在手机端通过微信或支付宝支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:订单满10减3,送腾讯会员7天或爱奇艺会员7天(随机)");Order order4 = new Order();order4.setCode("000004");order4.setAmount(new BigDecimal("131.7"));order4.setSource(OrderSourceEnum.MOBILE);order4.setPayMethod(OrderPayMethodEnum.WEIXIN);order4.setPayMember(MemberTypeEnum.NONACTIVATED);OrderConsumerResult orderConsumerResult41 = orderCpxService.orderService(order4);log.info("测试场景4结果:(在微信支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult41.getCode(), orderConsumerResult41.getAmountSpent(), orderConsumerResult41.getRight());order4.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);OrderConsumerResult orderConsumerResult42 = orderCpxService.orderService(order4);log.info("测试场景4结果:(在微信支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult42.getCode(), orderConsumerResult42.getAmountSpent(), orderConsumerResult42.getRight());log.info("测试场景5:(订单编号-000005)用户为黄金会员,在手机端通过微信支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:套餐支持满160减140优惠、减110、减100各一张优惠,同时送特别礼品(随机)");Order order5 = new Order();order5.setCode("000005");order5.setAmount(new BigDecimal("131.7"));order5.setSource(OrderSourceEnum.MOBILE);order5.setPayMethod(OrderPayMethodEnum.WEIXIN);order5.setPayMember(MemberTypeEnum.GOLD);OrderConsumerResult orderConsumerResult5 = orderCpxService.orderService(order5);log.info("测试场景5结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult5.getCode(), orderConsumerResult5.getAmountSpent(), orderConsumerResult5.getRight());log.info("测试场景6:(订单编号-000006)用户为铂金会员,在手机端通过本地钱包支付,当前实际购买总价为131.7元");log.info("按该场景实际符合策略:存入钱包500得800,套餐支持满100减50优惠、减40、减30各一张优惠,同时送特别礼品(随机)和内部优惠无限制满5减5(3张)");Order order6 = new Order();order6.setCode("000006");order6.setAmount(new BigDecimal("131.7"));order6.setSource(OrderSourceEnum.MOBILE);order6.setPayMethod(OrderPayMethodEnum.WALLET);order6.setPayMember(MemberTypeEnum.PLATINUM);OrderConsumerResult orderConsumerResult6 = orderCpxService.orderService(order6);log.info("测试场景6结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",orderConsumerResult6.getCode(), orderConsumerResult6.getAmountSpent(), orderConsumerResult6.getRight());}
}

测试结果展示


http://chatgpt.dhexx.cn/article/JdZJ6S9d.shtml

相关文章

​JAVA设计模式(六)——策略模式

下图为所有设计模式&#xff0c;带标记为重点掌握以及工作中常用到的&#xff1a; 策略模式是行为型设计模式之一&#xff0c;其作用是让一个类的行为或其算法可以在运行时更改&#xff0c;该模式也算是我比较熟悉的模式之一了&#xff0c;因为之前项目中有幸遇到大佬用过&…

java 策略模式例子_策略模式—Java实现(转)

1. 现实需求 客户有了新的需求&#xff0c;这时我们直接新增策略即可&#xff0c;改很少的代码。基本符合我们面向对象原则中的开闭原则(对扩展开放&#xff0c;对修改关系)&#xff0c;实现了高内聚低耦合。 2. 策略模式定义 策略模式&#xff0c;又叫算法簇模式&#xff0c;就…

Java设计模式(五)策略模式-在SpringBoot项目中的实际应用

文章目录 什么是策略模式优点缺点使用场景结构图 策略模式的简单示例策略模式的项目实战场景实现 小结 什么是策略模式 官话&#xff1a;策略模式(Strategy Pattern)&#xff1a; 定义一系列算法类&#xff0c;将每一个算法封装起来&#xff0c;并让它们可以相互替换&#xff…

java调用微信加密_java微信消息加解密

今天心血来潮就信手拈来学了下微信消息加解密的知识,忽然觉得微信真的好强大。可能在大部分项目微信消息的加解密都用不上,但是仍然不排除有使用到的情况,如涉及金钱方面的微信应用包括商城类、金融类还有其他安全级别要求很高的微信应用。针对这些情况我觉得还是有必要分享…

spring如何使用策略模式

这里使用登录做例子介绍如何实现登录的多种策略 上图是策略模式的基础模型。 context Context上下文角色&#xff0c;也叫Context封装角色&#xff0c;起承上启下的作用&#xff0c;屏蔽高层模块对策略、算法的直接访问&#xff0c;封装可能存在的变化。 Strategy 策略角色…

java设计模式实战-(反射+策略模式)

学完23种设计模式&#xff0c;相信很多同学都疑问&#xff0c;除了单例模式、工厂模式其他模式还有运用的场景吗&#xff1f; 现在这里就举一个例子。 首先策略模式需要先有了解&#xff0c;我们常用策略模式解决实际开发中的if else特别多的场景。但是在实际的开发中&#x…

Java23种设计模式之策略模式【普通写法以及spring中的写法】

目录 设计模式简介策略模式的简介普通写法案例基于注解式改造的案例优缺点策略模式的使用场景 设计模式简介 将设计者的思维融入大家的学习和工作中&#xff0c;更高层次的思考&#xff01; • 创建型模式&#xff1a; – 单例模式、工厂模式、抽象工厂模式、建造者模式、原型…

Java设计模式-策略模式(支付业务的优化)

一、什么情况下使用策略模式 功能类似的业务功能&#xff0c;避免创建多个接口&#xff0c;大量if else 判断逻辑&#xff0c;简化代码 二、我以支付为例&#xff08;支付方式有微信&#xff0c;支付宝支付&#xff09; 1、新建策略抽象类或者接口 public interface ThirdP…

java策略模式实战示例

现已放在gitee上,可以不下载直接参考一下即: https://gitee.com/zhang-xiao-xiang/zxx-pattern 日常碰到的业务概述 登录类型,支付类型,供应商渠道,不同等级会员享受的优惠券价格不一样,等等业务判断,大量if else导致拓展(侧重新增)极其困难,维护(侧重修改)自然是改起来头痛(…

【JAVA设计模式】策略模式

1.什么是策略模式&#xff1f; 策略模式是对算法的包装&#xff0c;是把使用算法的责任和算法本身分割开来&#xff0c;委派给不同的对象管理&#xff0c;相同的事情-----选择不用同方式 &#xff08;不同实现&#xff09;举例子&#xff0c;最终可以实现解决多重if判断问题。 …

java微信关注事件_java策略模式在接收微信事件推送上的具体应用

java策略模式&#xff0c;在我的认知中是根据不同选择执行不同的实现。通过if或者switch-case也能实现这种逻辑&#xff0c;但是代码冗余&#xff0c;可扩展性不强。 百度百科上的解释为策略模式作为一种软件设计模式&#xff0c;指对象有某个行为&#xff0c;但是在不同的场景…

Java设计模式之3种策略模式实现

一、什么是策略模式 所谓策略模式&#xff0c;就是定义了一组策略&#xff0c;分别封装在不同类中&#xff0c;每种策略都可以根据当前场景相互替换&#xff0c;从而使策略的变化可以独立于操作者。比如我们要去某个地方&#xff0c;会根据距离的不同来选择不同的出行方式&…

java8 策略模式_JAVA设计模式之策略模式

策略模式&#xff1a;在策略模式(Strategy Pattern)中&#xff0c;一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中&#xff0c;我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对…

git如何提交代码

代码提交 代码提交一般有五个步骤&#xff1a; 1.查看目前代码的修改状态 2.查看代码修改内容 3.暂存需要提交的文件 4.提交已暂存的文件 5.同步到服务器 1. 查看目前代码的修改状态 提交代码之前&#xff0c;首先应该检查目前所做的修改&#xff0c;运行git status命令 a)…

修改git提交时间

问题描述&#xff1a; 修改git提交记录的时间 git脚本&#xff1a; 此方法使用github上的开源工具完成。感谢无私的奉献者&#xff01; 操作步骤&#xff1a; github上下载文件 解压文件夹&#xff0c;把git-redate文件置于git安装目录的\mingw64\libexec\git-core文件夹…

git提交时常用命令

git提交时常用命令 一、git命令 -- 本地仓库与远程仓库建立链接 git init &#xff08;初始化本地仓库&#xff09; git remote add origin 远程仓库网址 git remote -v (查看远程仓库) git remote rm origin (删除远程分支)-- checkout多种用法 git checkout 文件名 &#…

git提交分支

1. git提交分支相关 在本地新建分支&#xff0c;保证和远程分支一样 git checkout -b 分支名如果分支已存在&#xff0c;只需要切换的话 git checkout 分支名提交前先把代码拉下来更新一下&#xff0c;确保不会覆盖别人的代码 git pull origin 远程分支(如果有)解决冲突 git …

Git 的提交

1 提交对象 理解Git分支&#xff0c;就需要了解提交及在提交过程中产生的提交对象&#xff08;commit object&#xff09;。在进行提交时Git会存储一个提交对象&#xff0c;其中包含一个指针&#xff0c;它指向此次提交时暂存内容的快照。除此之外&#xff0c;提交对象中还包含…

git代码提交

代码提交 代码提交一般有五个步骤&#xff1a; 1.查看目前代码的修改状态 2.查看代码修改内容 3.暂存需要提交的文件 4.提交已暂存的文件 5.同步到服务器 1. 查看目前代码的修改状态 提交代码之前&#xff0c;首先应该检查目前所做的修改&#xff0c;运行git status命令 a…

【git提交】流程

1- git提交&#xff08;分支合并&#xff09;操作流程&#xff1a; git 提交代码到自己的分支&#xff0c;并合并到主分支的完整流程 分支 sml &#xff0c; 主分支&#xff0c; master 分支msl上操作 git status git add . git commit -m ‘xxxxxx’ git pull origin master…