1、概念介绍:能力
在 Lattice 框架里,有一个叫做“能力(Ability)”的名词。 这里,我就这个名词做下我的理解。
《吕氏春秋·适威》:“民进则欲其赏,退则畏其罪,知其能力之不足也!”
方纪 《三峡之秋》二:“ 长江蕴藏着无限丰富的水能; 三峡 形成了天然的水利枢纽。这在我国的建设事业中,有可能变成一种巨大的能力。”
毛泽东在《纪念白求恩》中写道:“一个人能力有大小,但只要有这点精神,就是一个高尚的人。”
领导在平台开工会中提到:“一个平台能力有大有小,但只要架构优秀并持续改进,就是一个有希望的平台”
从上面的名家言论中可以看出,能力是对人、物等特定载体在某些场景下所表现出的一组行为的描述。
打个比方,我们在谈到“人”这个能力载体时,我们会说:
人具备改变世界的能力
人具备繁衍后代的能力
人具备扯淡的能力
相声演员(人的实例)具备说学逗唱的能力
体育运动员(人的实例)具备跑跳的能力
.............
2、能力的特征
如果我们再进一步细分,人这个载体又是由头、躯干、四肢、经血、脉络以及各种内脏组成。这些不同的部件,也可以做为一个载体提供一些更加细分、具体的能力。 比如,手这个载体,虽然他不具备繁衍后代的能力。但是手的能力有:可以弯曲的能力、可以负重的能力。 手弯曲的行为有手腕弯曲、手指弯曲,手的负重行为可以有以手掌方式负重、手指方式负重。 手这个部件的能力以及其行为虽然很有限,但是配合起来可以完成俯卧撑,最终支撑并实现了人类繁衍更加重要能力的实现。
所以,能力是可聚合的
说到了能力的行为,行为应该可以扩展的,扩展后的行为可以做更多的之前做不到的事。比如挠痒这个行为。其实挺简单,基本的挠痒动作就是用手去抓嘛,能抓得到的地方就去抓好了。但对于抓不到的地方,那怎么办? 你至少有两种选择:
换个行为,改用“蹭”,找个地方对准痒处使劲蹭..
还是用“挠”这个行为,不过得扩展一下手,在配合使用“不求人”完成对痒处的“挠”
所以,能力的行为是可扩展的
刚刚提到的手的能力及其行为,都原生的,与生俱来的。但是,我们是可以通过给人增加外挂,增加一些原本不具备的能力。比如,给眼睛配上夜视眼镜,人就具备了如猫一样可以夜视行为。 夜视眼镜的载体是眼睛,对看这个行为进行了扩展,从而使人具备夜视的能力。
所以,某些能力不一定是原生的,是可以通过工具后可叠加的
有时,你在某个方面可能真的很牛逼。比如,你唱歌很好、舞跳得棒、乒乓球也是一把好手。 可是,在程序员的世界里,你说了,大家也不见得会在意和记住。 但你不说,你就很有可能失去一次和女神一起去嗨歌的好机会。
所以,能力是一定要可呈现的,至少是对特定的场景/视图
如果能理解上面的概念,那么在回过头来看 Lattice 中关于能力接口的定义。
在 Lattice 中,我们定义了能力接口 IAbility,其接口定义有两个泛型,第一个是能力载体(AbilityTarget),另一个是能力的行为(也叫可扩展点 IExtensionPoints)。
public interface IAbility<BusinessExt extends IBusinessExt> {
/**
* @return current ability's unique code.
*/
String getCode();
/**
* @return current domain ability's instance unique code.
*/
String getInstanceCode();
AbilityContext getContext();
/**
* Whether current ability support current bizObject
*
* @return default return true.
*/
boolean supportChecking();
/**
* Whether current ability support customization from plugin's extension realization.
*
* @return true or false.
*/
boolean supportCustomization();
/**
* Whether current ability invoke getDefaultRealization() if the extension
* realizaiton not found in the Plugin.
*
* @return true or false.
*/
boolean hasDefaultExtension();
/**
* get the extension points' realization by business code.
*
* @return the ExtensionPoints realization.
*/
BusinessExt getDefaultRealization();
/**
* @param callback callback of the function.
* @param reducer The multi-result reduce policy.
* @return the result of extension customization.
*/
<T, R> R reduceExecute(ExtensionCallback<BusinessExt, T> callback,
@Nonnull Reducer<T, R> reducer);
}
复制代码
IAbility 能力中有两个关键的方法,一个是 supportChecking。 是用来检查在特定的业务约束下(bizCode),对于特定的载体,本能力是否支持?简单的打比方:
另一个方法是 getDefaultRealization() 获取行为的实例。 比如:
针对眼睛这个载体获取出 EmptyEyeExtension,那么就代表没有可用行为,比如盲人
针对眼睛这个载体取出的是基本实现 NormalEyeExtension,那么就可以完成基本看的行为
针对眼睛这个载体取出的是增强版 EnhancedEyeExtension,那么就可以完成夜视行为
关于如何具体定义一个能力的样例,可以参考 《Quick Start》
3、能力实例(Ability Instance)
在前面,我们介绍了“能力”这个概念。可以看到,能力就是对一种特定“模式”的抽象。 而针对这些“模式”能力抽象的具体实现,就称之为能力实例。
3.1 为什么会有多能力实例的场景?
我们以电商交易场景中,在买家下单的过程中,要计算运费、创建物流订单为例。 针对不同的场景有各种各样的物流方案,比如:
传统快递配送物流方式,这个不多介绍。(本例中,我们定义 NormalLogisticsAbility)
基于线下门店自提方式: 这种方式,货品发到门店,但交易订单产生后不会有实际物流,用户需要人肉到线下门店去提货。比如,买轮胎,轮胎不是寄到家里,而是自己开车去 4S 店去安装已购买好的轮胎。 (本例中,我们定义 OfflineLogisticsAbility)
虚拟物品发货方式:比如卡密等商品。。无论这些物流方式外在的差异有多大,但他们总有一些共性的功能是可以抽象成统一的接口。比如,无论何种运输方式,他都需要计算运费价格、需要在物流订单上保存物流信息等。平台则以面向接口的方式,去调用这些抽象好的接口,最终可实现平台内核稳定,同时也能支持未来新的运输方式的扩展。
3.2 如何定义多能力实例,并进行调用?
比如本例中,我们就可以抽象并定义个 “物流能力”,他包含物流运费计算以及物流订单信息补充的抽象接口定义,如下:
@Ability(name = "Logistics ability")
public abstract class LogisticsAbility<T extends IBusinessExt> extends BaseLatticeAbility<T> {
public LogisticsAbility(OrderLine orderLine) {
super(orderLine);
}
public abstract String getName();
public abstract long getPostFee();
public abstract void enrichLogisticsOrderDO(LogisticsOrderDO logisticsOrderDO);
}
复制代码
那么,针对普通物流和线下门店自提两种方式,我们可以定义两个具体的能力实例:NormalLogisticsAbility 和 OfflineLogisticsAbility。
在普通物流能力实例中,他会去实现 getPostFee() 抽象方法,并进行运费计算。 运费也允许业务开发人员进行扩展,如果没有特殊扩展,就采用一个默认的运费价格。如下:
@Priority(value = 300)
public class NormalLogisticsAbility extends LogisticsAbility<BlankNormalLogisticsExt> {
private final OrderLine orderLine;
public NormalLogisticsAbility(OrderLine orderLine) {
super(orderLine);
this.orderLine = orderLine;
}
@Override
public String getName() {
return "Normal";
}
@Override
public boolean supportChecking() {
String value = orderLine.getAttributes().get("lg_normal");
return StringUtils.equals(value, "true");
}
@Override
public long getPostFee() {
Long postFee = reduceExecute(extension -> extension.getCustomPostFee(orderLine),
Reducers.firstOf(Objects::nonNull));
return Optional.ofNullable(postFee)
.map(p -> postFee).orElse(orderLine.getDefaultPostFee());
}
......
}
复制代码
而线下门店自提的能力实例中,因为他不需要物流配送,运费始终为 0,如下:
@Priority(value = 400)
public class OfflineLogisticsAbility extends LogisticsAbility<BlankOfflineLogisticsExt> {
private final OrderLine orderLine;
public OfflineLogisticsAbility(OrderLine orderLine) {
super(orderLine);
this.orderLine = orderLine;
}
@Override
public String getName() {
return "Offline";
}
@Override
public long getPostFee() {
return 0;
}
@Override
public boolean supportChecking() {
String value = orderLine.getAttributes().get("lg_offline");
return StringUtils.equals(value, "true");
}
......
}
复制代码
这两个能力实例中,会有一个 supportChecking() 方法,来校验面对特定的“业务对象”,当前的能力实例是否生效。比如,我们的商品在发布中,往往可以设置多个物流模板。如果,某商品发布时设置了即可以物流配送,也可以到门店自提(比如大闸蟹)。那么用户在下单时,在下单页的物流方式的下拉框中,就可以看到多种方式,可以自行选择。
多能力实例定义完成后,我们在可以通过 Lattice 中的 getAllAbilities 和 getFirstMatchedAbility 两个方法,在运行期筛选出符合条件的能力实例。比如,本例中我们获取可用的运输方式以及该运输方式的运费价格为例,模拟了两次调用。第一次调用,只有普通配送方式,第二次调用追加了线下门店自提的方式。如下:
public class MultiAbilityInstStart {
public static void main(String[] args) {
Lattice.getInstance().setSimpleMode(true);
Lattice.getInstance().start();
Map<String, String> attributes = Maps.newHashMap();
attributes.put( "lg_normal", "true"); //只有普通配送方式的能力实例生效
doInvoke(attributes);
attributes.put( "lg_offline", "true");//追加线下自提配送方式的能力实例生效
doInvoke(attributes);
}
private static void doInvoke( Map<String, String > attributes){
System.out.println("--------------------------------------------");
OrderLine orderLine = new OrderLine();
orderLine.setUnitPrice(1000L);
orderLine.setBizCode("cloth");
orderLine.getAttributes().putAll(attributes);
List<LogisticsAbility> abilities = Lattice.getAllAbilities(LogisticsAbility.class, orderLine);
System.out.println("Available Logistic Type: " + abilities.stream().map(LogisticsAbility::getName).collect(Collectors.joining(",")));
for (LogisticsAbility ability : abilities) {
Long postFee = ability.getPostFee();
System.out.println("==>" + ability.getName() + " PostFee:" + postFee);
}
}
}
复制代码
运行结果如下:
--------------------------------------------
Available Logistic Type: Normal
==>Normal PostFee:10000
--------------------------------------------
Available Logistic Type: Normal,Offline
==>Normal PostFee:10000
==>Offline PostFee:0
复制代码
3.3 能力实例总结
本例中,我们是以物流能力抽象举例。发散一下,在优惠场景下,也是同样可以抽象 “基础营销工具能力”,并基于这个能力定义出各种实际的营销工具实例,进行模式级扩展。基于模式级扩展,可以有效的保障平台内核的稳定。
本样例代码可以通过访问:https://github.com/hiforce/lattice-sample/tree/main/lattice-multi-ability-instance 获取
Lattice 是一个强大的、轻量级的,面向业务定制高可扩展的业务管理框架。通过使用 Lattice 框架,可以对复杂的业务定制进行高效的组织与管理。关于 Lattice 框架简介,可参考:https://xie.infoq.cn/article/85fe94515a1ce0a8315550d62
评论