为了干掉 if else,我造了个轮子

最近没有在写新的文章,Kolin 系列和 DDD 系列都暂停了。原因是最近正在造轮子。

先送上轮子的项目地址:https://github.com/yanglifan/navi

起因

这个轮子名为 Navi,意为导航。之所以叫这个名字,是因为这个轮子的主要功能是提供了基于注解的组件选择功能。好比是为方法调用导航,选择合适的组件。故起名 Navi。

什么要造这个轮子?因为最近本人一直在做业务核心系统的重构,这些系统的代码历史都比较久远,复杂难懂。每个功能都有很多的逻辑分支,用来处理不同的业务场景。导致代码里充斥着各种条件语句。

这些代码虽然被频频吐槽,但因为它们承载了重要的业务功能,所以不能抛弃。同时,还有大量的新需求不断加入进来,所以这些代码还在不断修改。

这样的局面导致在现有系统上实现新业务的开发、测试速度越来越慢,成本越来越高。

思考

很多开发团队在面对这个问题的时候会选择推倒重来。这么做不是不行,但暂且不谈成本,首先要考虑的一个问题是,如何使用新的设计解决现有系统所存在的问题。

在我看来,解决很多复杂业务系统上述问题的一些通用方法有:

  1. 明确核心流程
  2. 组件(插件)化
  3. 强调组合大于继承的原则

上面三点的核心是组件化设计。即将复杂的业务系统拆分为不同的组件,以实现单一职责和开闭原则。这里要强调的是不要期望微服务能够解决上述问题。在没有将业务系统做组件化查分之前,强推微服务不仅不会解决上述问题,还会产生新的问题。

将业务系统组件化的最大问题是如何让一次请求走到正确的业务组件中,同时能够避免大量的条件语句。解决这一问题的选择有很多,比如各种设计模式。但设计模式不是开箱即用的,恰当地使用设计模式并不是一件简单的事情。

那有没有一种开箱即用的技术能够解决上述问题呢?在我十多年的工作中并没有发现很合适的工具。常见的如规则引擎、状态机等设计,虽然有可选的实现,但都显得过于复杂,难以使用。

解决

所以,为了解决上述问题,我设计开发了 Navi 这个项目。

Navi 的原理并不复杂:使用注解声明组件的匹配规则,使用时根据入参匹配规则,最终找出最合适的组件。

基本使用

一个简单的代码示例:

interface OrderCreateHandler { // 1
    void handle(Order order, OrderCreateRequest request);
}

@EqualMatcher(property = "clientType", value = "android") // 2
@VersionMatcher(range = "[1.0.0,2.0.0)") // 2
class AndroidV1Handler implments OrderCreateHandler { // 2
    void handle(Order order, OrderCreateRequest request) {
    }
}

public class OrderService {
    public OrderCreateResult createOrder(OrderCreateRequest req) {
        Order order = Order.create(req);
        
        OrderCreateHandler handler = selector.select(req, OrderCreateHandler.class); // 3
        if (handler != null) {
            handler.handle(order, req);
        }
    
        orderRepository.save(order);
    
        return OrderCreateResponse.create(order, req);
    }
}

第1点:定义一个接口。目前组件选择是以类为最小粒度。因为 Java 8 引入了 Functional Interface 和 Lambda,使得类级别的粒度也可以实现方法粒度的选择。

第2点:实现这个接口,并定义匹配规则。本例中,如果入参的 clientType 字段为 android,并且版本(默认 version)字段大于等于 1.0.0,小于 2.0.0 时,使用 AndroidV1Handler 这个组件。你可以定义多个实现相同接口的组件,以实现不同场景下的需求。

第3点:使用 selector (接口为 Selector,目前有 SimpleSelectorSpringBasedSelector 两个实现),传入请求和组件接口两个参数,根据组件实现上的匹配规则,选择出合适的组件。

组合匹配规则

除了直接使用匹配规则,还可以自定义组合匹配规则:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@EqualMatcher(property = "platform.tags")
@VersionMatcher(property = "appVersion")
@CompositeMatcherType
public @interface StoreViewHandler {
    String ALL_PLATFORMS = "*";

    @AliasFor(annotationFor = EqualMatcher.class, attributeFor = "value")
    String[] platform() default ALL_PLATFORMS;

    @AliasFor(annotationFor = VersionMatcher.class, attributeFor = "range")
    String clientVersionRange() default "";
}

上例就是组合匹配规则的定义方式。定义了组合匹配规则之后,你就可以直接使用你自己创建的匹配规则去替代之前的多个匹配规则。

其它

除了可以通过组合自定义匹配规则,还可以支持完全自定义的匹配规则,以及 repeatable 的匹配规则(Java 8 的 Repeatable 注解)

后续计划

目前这个轮子的一大问题是由于使用了反射,使得当被筛选的类较多时,性能损耗较多。所以 1.1.0 版本将重点通过缓存等手段,降低反射带来的性能损耗。在 1.2.0 版本中,将重点增加对泛型的支持。在 1.3.0 版本中,将重点支持动态的依赖注入。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,472评论 25 707
  • 早起永远都是个不过时的话题,我也相信在很多人心里都有一个早起的愿望,但总是难以付诸行动。头一天信誓旦旦要早起第二天...
    3aeeb1d6d616阅读 2,091评论 11 26
  • IS语音,是这两天接触到的一个直播平台,是在网上忘记是哪篇文章中看到的QQ加到然后让我进去的,3739房间,刚刚还...
    邦凯阅读 148评论 0 1
  • 我喜欢你,我的青春年华,你的回眸让我无比难忘。
    我不知道到如何是好阅读 101评论 0 2
  • 这几天零零散散将陈愉的《30岁前别结婚》看完,从她自身的一些经历,分享了自己对婚姻、事业、生活的态度。从小在美国华...
    Fangfangfei阅读 364评论 0 0