为了实现模块之间的解耦,我需要一个类似 Qt 中的信号槽机制 和 GDScript 中的信号机制。
使用场景:
有一个计数器和一个显示屏,当计数器更新时,需要显示屏同步显示更新的计数,后续可能需要将计数器的计数用于到其它地方。
先看看在没有实现信号订阅机制时的代码是怎么样写的。
class Counter
attr_accessor :value
def initialize(led_screen)
@led_screen = led_screen
end
def increment
@value = @value + 1
@led_screen.display(@value)
end
end
class LedScreen
def display(content)
# 显示到屏上,暂无实现
end
end
led_screen = LedScreen.new
counter = Counter.new(led_screen)
counter.increment
以上代码可以基本工作,但是我对这样的代码不满意。在对计数器 Counter 类进行单元测试时,需要引入 LedScreen 类,增加了复杂性。另外当有新的需求时,需要修改 Counter#increment 方法。
我不喜欢这样,我准备使用信号机制实现解耦。当需要将计数器 Counter 的值显示到其它地方,也不需要改动 Counter 的代码,计数器 Counter 就只是做它本应该做的事情。
首先不考虑具体的实现,先把我设想的 API 接口初步定下来,后续考虑使用 Ruby 的魔法来实现出来。用下面的代码来说明:
class Counter
attr_accessor :value
register_signal :value_changed
def increment
@value = @value + 1
emit_signal :value_changed, @value
# emit_signal 第0个参数是信号名,第1个参数是附带的消息
end
end
class LedScreen
# 默认调用 on_value_changed
# 也可以附加一个参数用来指定信号订阅的方法
# eg: subscribe_signal :value_changed, :when_value_changed
subscribe_signal :value_changed
def on_value_changed(value)
puts "Got counter value changed to #{value}"
# 更新到显示屏上
display(value)
end
def display(content)
# 显示到屏上,暂无实现
end
end
# Testcase
counter = Counter.new
counter.increment
led_screen = LedScreen.new
# 期望 LedScreen#on_value_changed 方法被调用
待续。也希望了解一下大家的想法。