写点什么

Lattice – 基于扩展点的多维度业务定制叠加

作者:rocky
  • 2022-12-01
    江苏
  • 本文字数:4525 字

    阅读完需:约 15 分钟

Lattice – 基于扩展点的多维度业务定制叠加

在上一篇 《Lattice - 面向高可扩展的业务框架》中,我们介绍了如果快速定义一个业务级扩展点,并能基于这个扩展点实现业务定制的插件化。同时,在例子中,我们也给出基于“业务身份”做业务定制逻辑的隔离。


在本文中,我们将进一步介绍,当一个扩展点有多个实现时,是会产生业务叠加的。业务逻辑叠加如果处理不好,往往就是代码腐化、噩梦的开始。关于业务叠加导致平台代码是如何腐化的,可以参考《一次代码腐化的演进过程》。


在读者看完上述两篇文章后,我会在本文分两部分来介绍业务叠加:

  • 能力 这个关键概念的介绍

  • 基于能力定义,实现业务叠加的样例


“能力”概念介绍


在 Lattice 框架里,有一个叫做“能力(Ability)”的名词。 这里,我就用这个名词做下我的理解。


《吕氏春秋·适威》:“民进则欲其赏,退则畏其罪,知其能力之不足也!”


《三峡之秋》二:“ 长江蕴藏着无限丰富的水能; 三峡 形成了天然的水利枢纽。这在我国的建设事业中,有可能变成一种巨大的能力。”


毛主席在《纪念白求恩》中写道:“一个人能力有大小,但只要有这点精神,就是一个高尚的人。”


领导在平台开工会中提到:“一个平台能力有大有小,但只要架构优秀并持续改进,就是一个有希望的平台”


从上面的名家言论中可以看出,能力是对人、物等特定载体在某些场景下所表现出的一组行为的描述。



打个比方,我们在谈到“人”这个能力载体时,我们会说:

  • 人具备改变世界的能力

  • 人具备繁衍后代的能力

  • 人具备扯淡的能力

  • 相声演员(人)具备说学逗唱的能力

  • 体育运动员(人)具备跑跳的能力

  • .............


能力的特征


如果我们再进一步细分,人这个载体又是由头、躯干、四肢、经血、脉络以及各种内脏组成。这些不同的部件,也可以作为一个载体提供一些更加细分、具体的能力。 比如,手这个载体,虽然他不具备繁衍后代的能力。但是手的能力有:可以弯曲的能力、可以负重的能力。 手弯曲的行为有手腕弯曲、手指弯曲,手的负重行为可以有以手掌方式负重、手指方式负重。 手这个部件的能力以及其行为虽然很有限,但是配合起来可以完成俯卧撑,最终支撑并实现了人类繁衍更加重要能力的实现。


所以,能力是可聚合的


说到了能力的行为,行为应该可以扩展的,扩展后的行为可以做更多的之前做不到的事。比如挠痒这个行为。其实挺简单,基本的挠痒动作就是用手去抓嘛,能抓得到的地方就去抓好了。但对于抓不到的地方,那怎么办? 你至少有两种选择:

  1. 换个行为,改用“蹭”,找个地方对准痒处使劲蹭..

  2. 还是用“挠”这个行为,不过得扩展一下手,再配合使用“不求人”完成对痒处的“挠”


所以,能力的行为是可扩展的


刚刚提到的手的能力及其行为,都是原生的,与生俱来的。但是,我们是可以通过给人增加外挂,增加一些原本不具备的能力。比如,给眼睛配上夜视眼镜,人就具备了如猫一样可以夜视行为。 夜视眼镜的载体是眼睛,对看这个行为进行了扩展,从而使人具备夜视的能力。


所以,某些能力不一定是原生的,是可以通过工具后叠加的


有时候,你在某个方面可能真的很牛逼。比如,你唱歌很好、舞跳得棒、乒乓球也是一把好手。 可是如果你不去介绍自己、不在特定场合去表现自己,那么谁也不知道你有啥能力。


所以,能力是一定是能被呈现的


如果能理解上面的概念,那么在回过头来看 Lattice 中关于能力接口的定义。

在 Lattice 中,我们定义了能力接口 IAbility,如下:


public interface IAbility<BusinessExt extends IBusinessExt> {
String getCode(); //能力编码
String getInstanceCode();
AbilityContext getContext(); //能力上下文
boolean supportChecking();//能否是否生效检查
BusinessExt getDefaultRealization(); //能力缺省默认实现
//能力执行器 <T, R> R reduceExecute(ExtensionCallback<BusinessExt, T> callback, @Nonnull Reducer<T, R> reducer);}
复制代码


IAbility 能力中有两个关键的方法,一个是 supportChecking。 是用来检查在特定的业务约束下(bizCode),对于特定的载体,本能力是否支持?简单地打比方:


  • “看”这个行为,去问“手”这个载体,应该得到 false 的结果

  • “看”这个行为去问“眼睛”,应该得到 true 的结果


另一个方法是 reduceExecute() ,他会根据能力上下文,去获取并执行特定的行为。 比如:

  • 针对眼睛这个载体获取出 EmptyEyeRealization,那么就代表没有可用行为,比如盲人

  • 针对眼睛这个载体取出的是基本实现 NormalEyeRealization,那么就可以完成基本看的行为

  • 针对眼睛这个载体取出的是增强版 EnhancedEyeRealization,那么就可以完成夜视行为


基于能力模型实现业务逻辑叠加


环境准备

您需要:

  1. 用于运行程序的 IDE(集成开发环境),比如 IntelliJ IDEA 或其类似工具;

  2. Java™ Development Kit (JDK),需要 JDK 8 及以上版本

  3. 完成阅读:Lattice - 面向高可扩展的业务框架


版本依赖

<dependency>      <groupId>org.hiforce.lattice</groupId>      <artifactId>lattice-model</artifactId>      <version>1.0.13</version></dependency><dependency>      <groupId>org.hiforce.lattice</groupId>      <artifactId>lattice-runtime</artifactId>      <version>1.0.13</version></dependency>
复制代码


Step 1:定义团购场景 “GroupBuyProduct” 产品

@Product(    code = GroupBuyProduct.GROUP_BUY_PRODUCT_CODE,     name = "Group Buy Trade Product")public class GroupBuyProduct extends ProductTemplate {
public static final String GROUP_BUY_PRODUCT_CODE = "lattice.productGroupBuyProduct";
@Override public boolean isEffect(ScenarioRequest request) { if (request instanceof BuyScenarioRequest) { boolean effect = StringUtils.equals("groupBuy", ((BuyScenarioRequest) request).getSource()); System.out.println("GroupBuyProduct effect status:" + effect); return effect; } return false; }}
复制代码


产品定义需要用 @Product 注解标识,同时需要继承 ProductTemplate 抽象类,并实现 isEffect 方法。团购产品的生效条件:


  1. 当前的场景是 “买家下单场景” ,即 ScenarioRequest 是 BuyScenarioRequest

  2. 如果渠道来源是 "groupBuy" ,当前产品则生效


Step 2:团购产品实现 “自定义商品单价”扩展点


在 《Lattice - 面向高可扩展的业务框架》,平台定义了一个 “自定义商品单价” 的扩展点。 在这里,我们让团购产品去实现这个扩展点,并假定在团购平台上,商品单价打 7 折。如下:

@Realization(codes = GroupBuyProduct.GROUP_BUY_PRODUCT_CODE)public class GroupBuyProductExt extends BlankOrderLinePriceExt {    @Override    public Long getCustomUnitPrice(OrderLine orderLine) {        return orderLine.getUnitPrice() * 700 / 1000; //only for sample.    }}
复制代码


注:这里的价格计算只是用于 DEMO,实际的价格计算不能用类型强制转换


Step 3:构造 “买家下单场景” 的请求


public class BuyScenarioRequest implements ScenarioRequest {
@Getter private final OrderLine orderLine;
@Getter @Setter private String source;
public BuyScenarioRequest(OrderLine orderLine) { this.orderLine = orderLine; }
@Override public OrderLine getBizObject() { return orderLine; }}
复制代码


Step 4:构造一次面向“买家下单场景”的业务调用过程

public static void doBusiness(String source) {        OrderLine orderLine = new OrderLine();        orderLine.setUnitPrice(1000L);        orderLine.setBizCode("business.b");        try {            Long unitPrice = new BizSessionScope<Long, OrderLine>(orderLine) {                @Override                protected Long execute() throws LatticeRuntimeException {                    //bla.bla.bla                    OrderLinePriceAbility ability = new OrderLinePriceAbility(orderLine);                    return ability.getCustomUnitPrice(orderLine);                }
@Override public BuyScenarioRequest buildScenarioRequest(OrderLine bizObject) { BuyScenarioRequest request = new BuyScenarioRequest(bizObject); request.setSource(source); //add some other info. return request; } }.invoke(); System.out.println("[Business B] overlay product unit price: " + unitPrice); } catch (LatticeRuntimeException ex) { System.out.println(ex.getErrorMessage().getText()); }catch (Throwable ex) { ex.printStackTrace(); } }
复制代码


一次业务调用过程,我们用 BizSessionScope 进行包装。 原因在于:

  1. 一个业务可以叠加非常多的产品;你可以想象,在电商生态中有一个 AppStore,每个业务都可以从 AppStore 中选择并安装产品;

  2. 一次业务调用,并不是业务安装的产品都会生效,比如本例中的“团购产品”,只有在下单渠道是“groupBuy”时才会生效;

  3. 一次业务调用中,我们需要过滤出本次会话实际生效的产品,并将生效的产品与业务叠加后,再进行扩展点调用以及多份扩展点实现的 Reduce


所以,BizSessionScope 会再被首次构造上,进行业务配置处理、生效的产品过滤、本次会话范围缓存初始化以及业务上下文初始化。


Step 5:执行样例

我们演示两次业务调用过程:

  1. 第一次业务的渠道来源是 null,商品价格就是商品单价 1000L

  2. 第二次业务的渠道来源是 groupBuy,团购平台要求商品单价必须打 7 折,产品的定制逻辑优先(这个一般是业务和平台产品签约时约定的)

public class LatticeOverlayProductSample {
public static void main(String[] args) { Lattice.getInstance().setSimpleMode(true); Lattice.getInstance().start();
System.out.println("---------------------------------------"); doBusiness(null); System.out.println("---------------------------------------"); doBusiness("groupBuy"); System.out.println("---------------------------------------"); } ......}
复制代码


运行结果如下:


---------------------------------------GroupBuyProduct effect status:false[Business B] overlay product unit price: 1000---------------------------------------GroupBuyProduct effect status:true[Business B] overlay product unit price: 700---------------------------------------
复制代码


我们可以看出,在团购产品生效时,自定义商品单价扩展点,返回的是 “团购产品” 的定制实现,即商品打了 7 折。


本文样例代码:https://github.com/hiforce/lattice-sample/tree/main/lattice-overlay-product

发布于: 刚刚阅读数: 3
用户头像

rocky

关注

架构师 2022-11-25 加入

专注面向高可扩展的业务框架、业务快速开发框架研究

评论

发布
暂无评论
Lattice – 基于扩展点的多维度业务定制叠加_架构_rocky_InfoQ写作社区