装饰器模式在项目中实战-创建领域对象缓存

需求背景:根据不同的场景,需要调用多个外部接口,每个外部接口耗时不等,我根据要求做成可动态增加的模式。

优点:用起来很方便,动态地追加功能,可以随意匹配调用接口的数量,且只调用一次方法即可,这会使业务代码非常清晰。配合回调函数和模板,可以实现动态地调用任意组合类型的接口。

缺点:代码有点多。会new出不少对象。因为本来掉接口的类完全可以用注入来的。现在的装饰器必须自己来new,否则无法保证灵活性。

下面是装饰器的一些类:

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉构件接口
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public interface IComponent<T> {
    /**
     * 〈一句话功能简述〉<br>
     * 〈功能详细描述〉
     *
     * @Param t
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本] (可选)
     **/
    void operation(T t);
}
/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉装饰类
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class Decorator<T> implements IComponent<T> {

    public IComponent component;

    public Decorator(IComponent component){
        this.component = component;
    }

    @Override
    public void operation(T t) {
        //noinspection unchecked
        this.component.operation(t);
    }
}
/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉会员属性基本构件
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class MemberAttrComponent implements IComponent<MemberAttrDO> {

    public MemberAttrComponent() {
        this.esOperator = SpringContextUtils.getBean("esOperatorImpl");
    }

    @Override
    public void operation(MemberAttrDO memberAttrDo) {
        //个性化实现
    }
}
/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉开通任性贷装饰类
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class RxLoanDecorator extends Decorator<MemberAttrDO> {

    public RxLoanDecorator(IComponent component) {
        super(component);
    }

    @Override
    public void operation(MemberAttrDO memberAttrDO) {
       //...省略个性化代码...
       super.operation(memberAttrDO);
    }
}
/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉首次购买理财装饰类
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class LicaiFirstBuyDecorator extends Decorator<MemberAttrDO> {

    public LicaiFirstBuyDecorator(IComponent component) {
        super(component);
    }

    @Override
    public void operation(MemberAttrDO memberAttrDO) {
        //...省略个性化代码...
        super.operation(memberAttrDO);
    }
}
/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉购买基金装饰类
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class FundBuyDecorator extends Decorator<MemberAttrDO> {

    public FundBuyDecorator(IComponent component) {
        super(component);
    }

    @Override
    public void operation(MemberAttrDO memberAttrDO) {
         //...省略个性化代码...
        super.operation(memberAttrDO);
    }
}
/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉首次登陆装饰类
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class FirstLoginDateDecorator extends Decorator<MemberAttrDO> {

    public FirstLoginDateDecorator(IComponent component) {
        super(component);
    }

    @Override
    public void operation(MemberAttrDO memberAttrDO) {
        //...省略个性化代码...
        super.operation(memberAttrDO);
    }
}

先上业务模板类:

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉查询会员所属用户组摸板类
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public abstract class AbstractMemberBelongGroupService {

    private static final Logger LOGGER = LoggerFactory.getLogger(QueryMemberBelongGroupForPortraitService.class);

    @Autowired
    QueryMemberBelongGroupDoService queryMemberBelongGroupDoService;

    @Autowired
    MemberAttrDoFactory memberAttrDoFactory;

    public ObjectResponse<Map<String, Object>> execute(ObjectRequest objectRequest) {
       final MemberAttrDO memberAttrDO = (MemberAttrDO) objectRequest.getEntity();
        ObjectResponse<Map<String, Object>> objectResponse = new ObjectResponse<>();
        // 工厂 + 切面 + 回调 => 创建领域对象
        FactoryCallBack<MemberAttrDO> factoryCallBack = () -> queryMemberAttr(memberAttrDO,objectResponse);
        memberAttrDoFactory.getInstance(memberAttrDO,factoryCallBack);
        LOGGER.info("会员编号:{},会员属性集合:{}", memberAttrDO.getUserNo(), JSON.toJSON(memberAttrDO.getMemberAttr()));
        // 构建groovy验证基本属性条件
        memberAttrDO.bulidMemberAttrMap();
        // 根据groovy规则计算出-会员标签集合
        Map<String, Object> memberLabelCodes = queryMemberBelongGroupDoService
                .queryMemberLabels(memberAttrDO.getMemberAttr());
        // 根据groovy规则计算出-会员所属用户组集合
        List<String> memberGroupList = queryMemberBelongGroupDoService.queryMemberBelongGroup(memberLabelCodes);
        Map<String, Object> resultMap = Maps.newHashMap();
        resultMap.put(MemberAttrDoConstants.LABEL_RESULT_LIST, memberAttrDO.getLabelCodeList(memberLabelCodes));
        resultMap.put(MemberAttrDoConstants.RESULT_LIST, memberGroupList);
        objectResponse.setObject(resultMap);
        LOGGER.info("查询会员所属用户组信息结束,返回报文:{}",JSON.toJSONString(objectResponse));
        return objectResponse;
    }

    protected abstract MemberAttrDO queryMemberAttr(MemberAttrDO memberAttrDO,ObjectResponse objectResponse);
}

业务子类一:

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉会员所属用户组服务(用于会员画像)
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
@Service
public class QueryMemberBelongGroupForPortraitService extends AbstractMemberBelongGroupService
        implements GenericService {

    @Override
    public ObjectResponse<Map<String, Object>> execute(ObjectRequest objectRequest) {
        return super.execute(objectRequest);
    }

    @Override
    protected MemberAttrDO queryMemberAttr(MemberAttrDO memberAttrDO, ObjectResponse objectResponse) {
        MemberAttrComponent memberAttrComponent = new MemberAttrComponent();
        // 查询会员属性
        memberAttrComponent.operation(memberAttrDO);
        // 如果不存在该会员直接返回
        if (MapUtils.isEmpty(memberAttrDO.getMemberAttr())) {
            objectResponse.setResponseEnum(ResponseCodeEnum.MEMBER_NOT_EXIST);
            return memberAttrDO;
        }
        Decorator<MemberAttrDO> decorator = new FirstLoginDateDecorator(new DefaultComponent());
        decorator = new LicaiFirstBuyDecorator(decorator);
        decorator = new RxLoanDecorator(decorator);
        decorator.operation(memberAttrDO);
        return memberAttrDO;
    }

}

业务子类二:

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉会员所属用户组服务(用于营销中台)
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
@Service
public class QueryMemberBelongGroupService extends AbstractMemberBelongGroupService implements GenericService {

    @Autowired
    MemberAttrDoRepository memberAttrRepository;

    @Autowired
    DecoratorFactory decoratorFactory;

    @Override
    public ObjectResponse<Map<String, Object>> execute(ObjectRequest objectRequest) {
        return super.execute(objectRequest);
    }

    @Override
    protected MemberAttrDO queryMemberAttr(MemberAttrDO memberAttrDO,ObjectResponse objectResponse) {
        // 查询现在全量用户组,找到所需要的所有属性
        List<AttrDefine> attrDefineList = memberAttrRepository.findMemberAttrInfo();
        List<String> attrCodeList = new ArrayList<>();
        attrDefineList.forEach(x -> attrCodeList.add(x.getAttrCode()));
        //获取属性的装饰器
        IComponent<MemberAttrDO> iComponent = decoratorFactory.getMemberAttrDecorator(attrCodeList);
        iComponent.operation(memberAttrDO);
        return memberAttrDO;
    }
}

统一获取和缓存领域对象的工厂:

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉领域对象获取工厂类
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
@Component
public class MemberAttrDoFactory {

    /**
     * 〈一句话功能简述〉<br>
     * 〈功能详细描述〉获取领域对象,这里只获取了ems_member_index索引里的属性
     *
     * @Param memberAttrDO 拦截器中会用来拼接缓存key
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本] (可选)
     **/
    @Cache(cacheKey = CacheConstants.MEMBER_ATTR_DOMIAN_REDIS_CACHE_KEY)
    public MemberAttrDO getInstance(MemberAttrDO memberAttrDO, FactoryCallBack<MemberAttrDO> callBack) {
        return callBack.createBySelf();
    }

}

领域对象缓存切面:

切面代码省略,即为切开MemberAttrDoFactory

领域对象缓存切面代码:

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉MemberDO缓存推送切面
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class MemberDoCacheAspect extends AbstractDomainCacheAspect<MemberAttrDO> {

    private static final Logger LOGGER = LoggerFactory.getLogger(MemberDoCacheAspect.class);

    @Autowired
    RedisOperator redisOperator;

    /**
     * 〈一句话功能简述〉<br>
     * 〈功能详细描述〉环绕通知
     *
     * @Param jp
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本] (可选)
     **/
    public MemberAttrDO aroundMethod(final ProceedingJoinPoint pjp) {
        return super.excute(pjp);
    }

    @Override
    protected MemberAttrDO cacheOperation(Object[] args, String cacheKey, ProceedingJoinPoint pjp) {
        Object o = args[0];
        // 参数不合法
        if (o == null || !(o instanceof MemberAttrDO)) {
            LOGGER.error("领域对象缓存不合法!");
            return null;
        }
        MemberAttrDO memberAttrDO = (MemberAttrDO) args[0];
        String userNo = memberAttrDO.getUserNo();
        String key = cacheKey + userNo;
        // 是否从redis读取标志
        boolean readFlag = DigitalConstants.STR_ONE
                .equals(SwitchScmCfg.getString(ScmConstants.MEMBER_DO_READ_REDIS_SWITCH));
        Map<String, String> stringMap = readFlag ? redisOperator.hgetAll(key) : new HashMap<>();
        Map<String, Object> map;
        if (MapUtils.isNotEmpty(stringMap)) {
            // 如果redis里存在属性map,直接构造,跳过原方法体
            map = memberAttrDO.getMemberAttr();
            stringMap.forEach(map::put);
            return memberAttrDO;
        }
        // 如果redis里不存在属性map,执行原方法体,并放入redis中
        try {
            Object result = pjp.proceed();
            if (result == null || !(result instanceof MemberAttrDO)) {
                LOGGER.info("执行结果获取的领域对象不合法!");
                return null;
            }
            map = ((MemberAttrDO) result).getMemberAttr();
            memberAttrDO.setMemberAttr(map);
            // 是否写入redis标志,默认原方法体执行出来的数据是正确的,不对map做校验
            boolean writeFlag = DigitalConstants.STR_ONE
                    .equals(SwitchScmCfg.getString(ScmConstants.MEMBER_DO_WRITE_REDIS_SWITCH));
            if (writeFlag) {
                map.forEach((k, v) -> stringMap.put(k, v instanceof String ? (String) v : JSON.toJSONString(v)));
                redisOperator.hmset(key, stringMap);
                redisOperator.expire(key,
                        CacheScmCfg.getInteger(CacheConstants.MEMBER_ATTR_DOMIAN_REDIS_CACHE_EXPIRE_TIME));
            }
        } catch (Throwable throwable) {
            LOGGER.error("执行方法出现异常!");
        }
        return memberAttrDO;
    }
}

装饰器工厂:

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
@Component
public class DecoratorFactory {

    Logger LOGGER = LoggerFactory.getLogger(DecoratorFactory.class);

    /**
     * 会员属性 包含了在es中的ems_member_attr里的会员属性
     */
    @Resource(name = "memberAttrList")
    private List<String> memberAttrList;

    /**
     * 基金产品购买标记
     */
    private static final String FUND_BUY = "FUND_BUY";

    /**
     * 理财首购标记
     */
    private static final String LICAI_FIRST_BUY = "LICAI_FIRST_BUY";

    /**
     * 开通任性贷标记
     */
    private static final String OPEN_RX_LOAN = "OPEN_RX_LOAN";

    /**
     * 〈一句话功能简述〉<br>
     * 〈功能详细描述〉匹配属性code对应的装饰器
     *
     * @Param attrCodeList 属性code集合
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本] (可选)
     **/
    public IComponent<MemberAttrDO> getMemberAttrDecorator(List<String> attrCodeList) {
        LOGGER.debug("匹配会员属性对应的装饰器...");
        IComponent<MemberAttrDO> iComponent = new DefaultComponent();
        boolean flag = false;
        boolean need = true;
        for (String attrCode : attrCodeSet) {
            if (flag) {
                if (memberAttrList.contains(attrCode)) {
                    iComponent = new MemberAttrComponent();
                }
                flag = false;
            } else {
                if (need && memberAttrList.contains(attrCode)) {
                    flag = true;
                    need = false;
                    continue;
                }
                switch (attrCode) {
                    case FUND_BUY:
                        iComponent = new FundBuyDecorator(iComponent);
                        break;
                    case LICAI_FIRST_BUY:
                        iComponent = new LicaiFirstBuyDecorator(iComponent);
                        break;
                    case OPEN_RX_LOAN:
                        iComponent = new RxLoanDecorator(iComponent);
                        break;
                    default:
                        LOGGER.warn("出现未知的会员属性:{},装饰器工厂类无法识别!",attrCode);
                }

            }
        }
        LOGGER.debug("匹配会员属性对应的装饰器结束:{}", JSON.toJSON(iComponent));
        return iComponent;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值