jface databinding:更简单的ISideEffect实现多目标单边数据绑定塈其原理分析

Eclipse 4.6 提供了名为 ISideEffect的数据绑定工具. ISideEffect可以实现当一个或多个观察对象(IObservable)改变时执行特定代码。 ISideEffect很像一个侦听器,但它却不需要开发者像侦听器那样作任何依附对象的动作(addChangeListener/removeChangeListener)。当被监控的观察对象改变时它会自动反应执行指定的代码。 口说无凭,还是举个简单的栗子吧:

下面这个代码片中,当userFirstName或userLastName 改变时会自动更新Label 对象中的内容,

IObservableValue<String> userFirstName = ...
IObservableValue<String> userLastName = ...
Label yourUsername = ...

ISideEffect sideEffect =
  ISideEffect.create(
       () -> {return "Your username is: " + userFirstName .getValue()+"."+userLastName.getValue();},// 返回观察对象列表
       yourUsername::setText// 要执行的动作
       );

在这段代码中,一般做数据绑定必须的WidgetProperties,DataBindingContext对象和bindValue方法都没有粗线,就一个简单的ISideEffect.create方法就完成了userFirstName和userLastName -> yourUsername 之间的单向数据绑定。 刚看到的这个例子时,我震惊了。。。尼玛这是何方神器啊?好牛逼啊。 深入研究ISideEffect.create方法的源码,才搞明白原理: 要完全讲清楚它的机制说起来太麻烦也没那能力,就只简单说说它的实现原理了几个关键点吧。 说到底,ISideEffect的实现基本原理还是通过加载侦听器(addChangeListener)到被观察对象来实现数据绑定的。 只不过这载侦听器的动作以及很多相关动作都在开发者没有察觉的情况下被悄悄完成了。 首先调用create方法后,ISideEffect会自动分析并获取第一个参数中涉及的所有被观察对象(IObservable)。 怎么获取的呢? 这就要说到另一个神器ObservableTracker,ObservableTracker中的runAndMonitor方法有一个神奇的功能就是可以返回第一个参数中所有被读取过的IObservable对象列表。 很显然上面的例子中,第一个参数是个lambda表达式,() -> {return "Your username is: " + userFirstName.getValue()+"."+userLastName.getValue();},这个表达式用调用了userFirstName.getValue()userLastName.getValue()。 于是ISideEffect就知道需要监控userFirstName 和userLastName这两个Observable对象。 然后就会在userFirstName 和userLastName上添加ChangeListener,当userFirstName 和userLastName中任何一个对象改变时,会先执行第一参数指定的lambda表达式,返回”Your username is: xxxx”,然后执行第二个表达式,将yourUsername内容设置为第一个lambda表达返回的值。

那么再问一句:ObservableTracker.runAndMonitor又是如何能分析出所有被观察对象的呢? 简单说,这完全依赖于另一个方法的配合ObservableTracker.getterCalled,所有的IObservable对象都会在getter方法中调用ObservableTracker.getterCalled,是它收集并向ObservableTracker透露了消息,才让ObservableTracker.runAndMonitor达成目的。 下面是ObservableTracker.getterCalled的调用层次结构图

下面是ObservableTracker.getterCalled的源码

    public static void getterCalled(IObservable observable) {
        if (observable.isDisposed())
            Assert.isTrue(false, "Getter called on disposed observable " //$NON-NLS-1$
                    + toString(observable));
        Realm realm = observable.getRealm();
        if (!realm.isCurrent())
            Assert.isTrue(false, "Getter called outside realm of observable " //$NON-NLS-1$
                    + toString(observable));

        if (isIgnore())
            return;

        Set<IObservable> getterCalledSet = currentGetterCalledSet.get();
        // 下面这句getterCalledSet.add(observable)就像间谍一样把当前的observable对象提供给了ObservableTracker
        if (getterCalledSet != null && getterCalledSet.add(observable)) {
            // If anyone is listening for observable usage...
            IChangeListener changeListener = currentChangeListener.get();
            if (changeListener != null)
                observable.addChangeListener(changeListener);
            IStaleListener staleListener = currentStaleListener.get();
            if (staleListener != null)
                observable.addStaleListener(staleListener);
        }
    }

关于ObservableTracker.getterCalled更详细的说明参见本文最后附参考资料中的《Tracked Getter》

ISideEffect与DataBindingContext 的区别

ISideEffect与原有的DataBindingContext binding机制相比有着明显区别,它们之间一种相互补充的关系: DataBindingContext实现的是一对一的数据绑定,支持双向数据同步更新,支持数据类型转换、数据验证,几乎方方面面都照顾到了,可以看作是个大而全的体系。 但是这个大而全的体系并不是在所有的场景下用起来都顺手,有时还显得臃肿, 比如我们在很多应用场景下并不需要双向数据同步更新,只需要单向的控制,也不需要类型转换和数据验证,这时DataBindingContext复杂的调用方式就显得麻烦臃肿。(参见我的下一篇博客《jface databinding: Radio Button group及ISideEffect绑定数据对象的例子》中用ISideEffect控制组件visiable状态的例子)。 再比如当多个Observable对象更新时都要同时更新同一个数据对象时(比如状态条),DataBindingContext就要创建多个绑定,好麻烦,这个数据对象就会被短时间内更新多次。 再回头来看ISideEffect,DataBindingContext的缺点就是ISideEffect的优点,我们可以把ISideEffect视为支持一对一、一对多、多对一、多对多的单向数据绑定机制(自然不支持双向数据更新),因为它不局限于一对一的灵活性,所以它没有数据类型转换、数据验证的概念。换个角度来看,可以把ISideEffect理解为一个触发器,当一个或多个Observable对象改变时自动触发执行指定的动作,具体是什么动作,可以是任意的,不一定是数据更新,播放一段音乐也是可以的。。。

前面说过了,在多对一、多对多的场景下,当多个观察对象(IObservable)更新时,ISideEffect会自动响应,所以在短时间内有多个观察对象(IObservable)更新的的情况下,ISideEffect只响应一次,可以避免过多的更新动作,这是DataBindingContext做不到的。

参考资料: 《Tracked Getter》 《ObservableTracker》 《Using the ISideEffect API》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python研发

Django|第一部

  注明的MVC模式:所谓MVC就是把web应用分为模型(M),控制器(C),视图(V)三层;他们之间以一种插件似的,松耦合的方式连接在一起.

1093
来自专栏葡萄城控件技术团队

程序员Web面试之JSON

JSON是什么? JSON(JavaScript对象表示法), 是在网络通信下,常用的一种数据表达格式,它有助于我们于一个自描述的,独立的和轻的方式呈现并交换数...

20210
来自专栏程序之美

第十一节 netty前传-NIO 和IO对比

两者有两点最明显也是最主要的区别 IO:面向流、阻塞模式 NIO:面向缓冲、非阻塞模式

684
来自专栏我叫刘半仙

原自己手写一个Mybatis框架(简化)

       继上一篇手写SpringMVC之后,我最近趁热打铁,研究了一下Mybatis。MyBatis框架的核心功能其实不难,无非就是动态代理和jdbc的操...

1.1K6
来自专栏文大师的新世界

Java面试

自己经验有限,篇幅也有限,这里只是记录一些比较容易混淆或有难度和一些易忘的技术知识点,里面有一些也是面试阿里经常会被问到的问题,但是不保证答案全部正确,有错误的...

803
来自专栏Android群英传

快刀斩乱码—— Dagger2没有想象的那么难

862
来自专栏贾老师の博客

【译】进程的内存剖析

1475
来自专栏GreenLeaves

Apater适配器模式(结构型模式)

what is Apater?适配,这个概念在生活中无处不在,比如你的iphone 4手机充电器坏了,这是时候只有一个iphone 8的充电器,两个充电器的头并...

482
来自专栏牛客网

知识总结:设计模式总结(C++和Python实现)前言案例实现 创建型模式 结构型模式行为型模式对比总结

前言 GoF的23种设计模式,包括创建型、结构型和行为型,其涵盖了面向对象思想的精髓以及诸多细节。本文结合《设计模式》和《大话设计模式》,并用C++和Pytho...

4728
来自专栏iOS技术杂谈

深入源码理解YYCache 、SDWebImage、AFNetworking、NSCache 缓存方式与对比

深入源码理解YYCache 、SDWebImage、AFNetworking、NSCache 缓存方式与对比 转载请注明出处 https://cloud.ten...

5337

扫码关注云+社区