全文共3564字,预计学习时长11分钟

构造函数(constructor)和观察者模式,谁略胜一筹呢?这要看情况。

谁属于谁?

通常我们使用构造函数(constructor)参数连接两个组件。例如,在构造图形表面时可以非常清楚地看到此过程。比如以下源代码:

public classSubView {

private MainView mainView;

public SubView(MainView mainView) {

this.mainView = mainView;

}

public void buttonClicked(String input) {

mainView.setInputValue(input);

}

}

此子组件通过构造函数(constructor)获取周围的主组件。此组件的实例只是值传输所需的。该值是子组件内部用户交互的结果,以及周围元素内部消耗的结果。

此过程会面对各种挑战,但这些挑战可以避免。首先,此子组件是硬绑定到主要元件的。但这种硬耦合类型没有意义,因为这种结合纯粹基于生成值的使用。

此外,子组件的测试更加困难,因为必须使用主组件的实例或相应的MOCK。同样,这是一个附加要求,增加了复杂性,同时阻碍了各个组件的提取。那么,此时如何将其添加到项目中而无需依赖其他框架呢?

图源:pexels

经典方法:观察者模式(Observer)

有一个简单的设计模式可以提供帮助。那就是观察者模式。

public classObservable {

private final Map> listeners = new ConcurrentHashMap<>();

public void register(KEY key, Consumer listener) {

listeners.put(key, listener);

}

public void unregister(KEY key) {

listeners.remove(key);

}

public void sentEvent(VALUE event) {

listeners.values()

.forEach(listener -> listener.accept(event));

}

}

其基本原理包括三个交互过程。在映射(map)中,会为给定的密钥而存储(消费者(consumer))信息。消费者(consumer)是事件的利用率单位,或者是由映射(map)的第二种类型定义所定义的输入类型。可以使用密钥注册,以后如果需要的话,还可以从此映射(map)中再次删除关联的消费者(consumer)。

待处理的数据包会用post event将其发送给此时所有已注册的消费者(consumer)。实际上,映射(map)中的所有现有消费者(consumer)都将以未定义的顺序处理值。发送的事件本身必须是不可变的。

注册

为了简化注册和退订的过程,可以稍微修改观察者设计模式。

public interfaceRegistration {

void remove();

}

public classRegistry {

private final Map> listeners = new ConcurrentHashMap<>();

public static Registry instance() {

return new Registry<>();

}

public Registration register(KEY key, Consumer listener) {

listeners.put(key, listener);

return () -> listeners.remove(key);

}

public void sentEvent(VALUE event) {

listeners.values()

.forEach(listener -> listener.accept(event));

}

}

为此,定义了一个名为Registration 的函数接口,它只能从注册表中自行删除。这同样适用于相应的注销过程。Registration是注册过程本身的返回值。

事件数据的处理与Observable的处理方法相同。JUnit5测试的实际用途如下所示:

final Registry eventBus = Registry.instance();

finalString expected = "message 001";

final AtomicInteger counter = new AtomicInteger(0);

finalString key01 = "Consumer-01";

finalString key02 = "Consumer-02";

final Registration register01 = eventBus.register(key01, (event) -> {

assertEquals(expected, event.getMessage());

counter.incrementAndGet();

});

final Registration register02 = eventBus.register(key02, (event) -> {

assertEquals(expected, event.getMessage());

counter.incrementAndGet();

});

eventBus.sentEvent(new Event(expected, ""));

Assertions.assertEquals(2, counter.get());

register01.remove();

eventBus.sentEvent(new Event(expected, ""));

Assertions.assertEquals(3, counter.get());

组件耦合

让我们从子组件开始吧。该元件将不再链接到主组件。此例使用的是Registry类的常规静态eventbus。当然,还可以为每个组件使用event bus,这将进一步分离组件。

public classSubView {

public void buttonClicked(String input) {

EVENT_BUS.sentEvent(new Event(input));

}

public Registration register(String key, Consumer listener) {

return EVENT_BUS.register(key, listener);

}

}

如果另一个组件需要使用虚拟用户交互的值,则可以在子组件的实例注册。

public staticclass MainView {

//for demo public

public SubView subView = new SubView();

private Registration registration = subView.register("keyXYZ",

e -> inputValue = e.getValue());

private String inputValue;

public String getInputValue() {

return inputValue;

}

public void release() {

registration.remove();

}

}

相应的JUnit5测试如下所示。

final MainView mainView = new MainView();

finalString inputValue = "inputValue";

//subview is public for demo

mainView.subView.buttonClicked(inputValue);

Assertions.assertEquals(inputValue, mainView.getInputValue());

图源:pexels

总结

通过几行源代码,不仅使组件的耦合更好,而且还简化了各个元件的测试,从而不再需要mock。增加的抽象允许一个以上的组件在此处显示的子组件上注册。当然,此时不应忘记注销注册表以使垃圾收集器正常运行。

祝大家编码愉快!

留言点赞关注

我们一起分享AI学习与发展的干货

如转载,请后台留言,遵守转载规范

构造函数未定义_构造函数(constructor)和观察者模式,谁略胜一筹呢?相关推荐

  1. java构造函数未定义_错误:隐式超级构造函数Person()未定义.必须显式调用另一个构造函数....

    今天写程序的时候发现了一个错误: public class Test { public static void main(String[] args) { Student s1 = new Stude ...

  2. vue中渲染对象中属性时显示未定义_揭开 vue 背后的秘密(1)

    ​昨天写了关于 react 如何j将 jsx 渲染到界面,今天朋友让我来谈谈 vue,个人对于 vue 研究还是最近的事.说到 angular.react 和 vue 这三个前端主流框架,最先先接触的 ...

  3. python老是报参数未定义_浅谈Python程序的错误:变量未定义

    Python程序的错误种类 Python程序的错误分两种.一种是语法错误(syntax error).这种错误是语句的书写不符合Python语言的语法规定.第二种是逻辑错误(logic error). ...

  4. python未定义_未定义Python函数

    当我运行服务器(FLASK app)时,遇到一个函数未定义的错误: 名称错误:未定义名称"format_date" 这是我目前为止的所有代码:from flask import F ...

  5. java 未定义_未定义,未指定和实现定义的行为

    Undefined behavior 是C和C语言的一个方面,对于来自其他语言的程序员来说可能会令人惊讶(其他语言试图更好地隐藏它) . 基本上,即使许多C编译器不会报告程序中的任何错误,也可以编写不 ...

  6. mysql未定义_以mysql_开始的未定义引用错误

    以mysql_开始的未定义引用错误 作者:佚名 来源:CNZZ 2008-12-26 当你链接到应用程序以使用MySQL客户端库时,可能会遇到以mysql_开始的未定义引用错误,如下所示: /tmp/ ...

  7. mysql未定义_以mysql_开始的未定义引用错误_MySQL

    以mysql_开始的未定义引用错误_MySQL 作者:小涵 | 来源:互联网 | 2018-04-19 15:15 阅读: 1682 以mysql_开始的未定义引用错误 当你链接到应用程序以使用MyS ...

  8. mysql未定义_解析:以mysql_开始的未定义引用错误

    当你链接到应用程序以使用MySQL客户端库时,可能会遇到以mysql_开始的未定义引用错误,如下所示: /tmp/ccFKsdPa.o: 在函数`main'中: /tmp/ccFKsdPa.o(.te ...

  9. python未定义_浅谈Python程序的错误:变量未定义

    Python程序的错误种类 Python程序的错误分两种.一种是语法错误(syntax error).这种错误是语句的书写不符合Python语言的语法规定.第二种是逻辑错误(logic error). ...

最新文章

  1. 计算器,利用灰度图的顶帽扣出数字
  2. 图片保存路径更改 python
  3. python gpiozero,树莓派远程GPIO调试(Python + pigpio版)
  4. C语言union关键字
  5. IoT -- (四) 物联网系统架构介绍
  6. 前台发送 ajax 请求到后台传递数组参数
  7. php.ini设置详解
  8. Mask-RCNN校验结果计算mAP值
  9. python数据类型-列表练习
  10. iOS UICollectionView 注册步骤、使用方法以及 各种问题 和坑点
  11. 吃芋头相当于注射免疫球蛋白
  12. SQLServer -ServiceBroker
  13. Windows7 开机自启脚本
  14. 牛客网Java选择题练习
  15. 有什么好用的配音软件?最好是免费的
  16. L1-040. 最佳情侣身高差
  17. 前后端分离-----SEO优化
  18. 个人永久性免费-Excel催化剂功能第87波-将批量发送邮件做到极致化,需借力Outlook...
  19. 5944的空间骗人,黑心
  20. Unity3D之Android加密DLL与破解DLL

热门文章

  1. Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
  2. pytorch和Numpy的区别以及相互转换
  3. python opencv多边形掩膜
  4. java链表寻找中间节点
  5. mysql 分页优化
  6. 什么是mini GBIC,mini GBIC与SFP光模块有什么区别?
  7. 三、神兽变变变(下)
  8. 10版微机监测怎么显示服务器,铁路信号网络版微机监测系统的研究
  9. php排除无效字查询,php删除无效的字符
  10. php 实现联想式 搜索,PHP实现搜索联想功能(基于字典树算法)