构造函数未定义_构造函数(constructor)和观察者模式,谁略胜一筹呢?
全文共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)和观察者模式,谁略胜一筹呢?相关推荐
- java构造函数未定义_错误:隐式超级构造函数Person()未定义.必须显式调用另一个构造函数....
今天写程序的时候发现了一个错误: public class Test { public static void main(String[] args) { Student s1 = new Stude ...
- vue中渲染对象中属性时显示未定义_揭开 vue 背后的秘密(1)
昨天写了关于 react 如何j将 jsx 渲染到界面,今天朋友让我来谈谈 vue,个人对于 vue 研究还是最近的事.说到 angular.react 和 vue 这三个前端主流框架,最先先接触的 ...
- python老是报参数未定义_浅谈Python程序的错误:变量未定义
Python程序的错误种类 Python程序的错误分两种.一种是语法错误(syntax error).这种错误是语句的书写不符合Python语言的语法规定.第二种是逻辑错误(logic error). ...
- python未定义_未定义Python函数
当我运行服务器(FLASK app)时,遇到一个函数未定义的错误: 名称错误:未定义名称"format_date" 这是我目前为止的所有代码:from flask import F ...
- java 未定义_未定义,未指定和实现定义的行为
Undefined behavior 是C和C语言的一个方面,对于来自其他语言的程序员来说可能会令人惊讶(其他语言试图更好地隐藏它) . 基本上,即使许多C编译器不会报告程序中的任何错误,也可以编写不 ...
- mysql未定义_以mysql_开始的未定义引用错误
以mysql_开始的未定义引用错误 作者:佚名 来源:CNZZ 2008-12-26 当你链接到应用程序以使用MySQL客户端库时,可能会遇到以mysql_开始的未定义引用错误,如下所示: /tmp/ ...
- mysql未定义_以mysql_开始的未定义引用错误_MySQL
以mysql_开始的未定义引用错误_MySQL 作者:小涵 | 来源:互联网 | 2018-04-19 15:15 阅读: 1682 以mysql_开始的未定义引用错误 当你链接到应用程序以使用MyS ...
- mysql未定义_解析:以mysql_开始的未定义引用错误
当你链接到应用程序以使用MySQL客户端库时,可能会遇到以mysql_开始的未定义引用错误,如下所示: /tmp/ccFKsdPa.o: 在函数`main'中: /tmp/ccFKsdPa.o(.te ...
- python未定义_浅谈Python程序的错误:变量未定义
Python程序的错误种类 Python程序的错误分两种.一种是语法错误(syntax error).这种错误是语句的书写不符合Python语言的语法规定.第二种是逻辑错误(logic error). ...
最新文章
- 计算器,利用灰度图的顶帽扣出数字
- 图片保存路径更改 python
- python gpiozero,树莓派远程GPIO调试(Python + pigpio版)
- C语言union关键字
- IoT -- (四) 物联网系统架构介绍
- 前台发送 ajax 请求到后台传递数组参数
- php.ini设置详解
- Mask-RCNN校验结果计算mAP值
- python数据类型-列表练习
- iOS UICollectionView 注册步骤、使用方法以及 各种问题 和坑点
- 吃芋头相当于注射免疫球蛋白
- SQLServer -ServiceBroker
- Windows7 开机自启脚本
- 牛客网Java选择题练习
- 有什么好用的配音软件?最好是免费的
- L1-040. 最佳情侣身高差
- 前后端分离-----SEO优化
- 个人永久性免费-Excel催化剂功能第87波-将批量发送邮件做到极致化,需借力Outlook...
- 5944的空间骗人,黑心
- Unity3D之Android加密DLL与破解DLL