我有一个Ruby对象,它将事件分派给第三方服务:
class Dispatcher
def track_event(e)
ThirdPartyService.track e.id, e.name, my_api_key
end
end
使用ThirdPartyService
可能会引发错误(例如,如果没有可用的网络连接)。 Dispatcher
的消费者通常适合决定如何处理这些消费者,而不是Dispatcher
本身。
我们如何以对象出现以使用Dispatcher
的方式来装饰Dispatcher
的使用,但所有异常都会被捕获并记录下来?也就是说,我希望能够写下:
obj.track_event(...)
但要抓住例外情况。
答案 0 :(得分:2)
Thoughtbot在different ways to implement decorators in Ruby上有一篇很棒的博文。这个("模块+扩展+超级装饰")特别简洁:
module ErrorLoggingMixin
def track_event(event)
super
rescue ex
Logger.warn(ex)
end
end
obj = Dispatcher.new # initialize a Dispatcher as usual
obj.extend(ErrorLoggingMixin) # extend it with the new behavior
obj.track_event(some_event) # call its methods as usual
博客文章列出了这些优点和缺点:
此实施的好处是:
- 它通过所有装饰者代理
- 它具有所有原始界面,因为它是原始对象
此实现的缺点是:
- 不能在同一个对象上多次使用相同的装饰器 *很难分辨哪个装饰者添加了功能
我建议您阅读帖子的其余部分,了解其他实施方案,并做出最适合您需求的选择。
答案 1 :(得分:1)
我最接近的想法是在一个方法中包含track_event
的用法,该方法提供了一个捕获异常的块,如下所示:
module DispatcherHelper
def self.dispatch(&block)
dispatcher = Dispatcher.new
begin
yield dispatcher
rescue NetworkError
# ...
rescue ThirdPartyError
# ...
end
end
end
这样我们就可以了:
DispatcherHelper.dispatch { |d| d.track_event(...) }
我能看到的另一种选择是模仿track_event
的签名,以便你得到:
module DispatcherHelper
def self.track_event(e)
begin
Dispatcher.new.track_event(e)
rescue NetworkError
# ...
rescue ThirdPartyError
# ...
end
end
end
但我不太喜欢它,因为它将签名连接在一起。
答案 2 :(得分:0)
这样做的方法不止一种。实现目标的一种方法是进行小规模的重构,将异常提升方法重新定义为“谨慎”#34;方法,将其设为私有,并使用更安全的"公共接口中的无爆炸方法。例如:
class Dispatcher
def track_event(e)
result = track_event! e rescue nil
result ? result : 'Exception handled here.'
end
private
def track_event! e
ThirdPartyService.track e.id, e.name, my_api_key
end
end
使用重构代码会在ThirdPartyService引发异常时产生以下内容:
Dispatcher.new.track_event 1
#=> "Exception handled here."
当然还有其他方法可以解决这类问题。很大程度上取决于您的代码试图表达的内容。因此,您的里程可能会有所不同。