【问题标题】:How to decorate a method in Ruby without alias_method_chain如何在没有 alias_method_chain 的情况下在 Ruby 中装饰方法
【发布时间】:2011-05-20 04:41:12
【问题描述】:

我们都知道,如果目标类是由模块组成的,你可以在一个新的模块中调用super。但是如果是类中的普通方法呢?

class Logger
  def message(msg)
    puts msg
  end
end

说,Logger 是我无法更改的类(例如,它在 gem 中)。 我希望 Logger 在每条消息之前放置一个“================”行。我该如何以的方式做到这一点?遗产?聚合?怎么样?

【问题讨论】:

  • 你可以打开类或使用 ol' 装饰器模式来做到这一点。

标签: ruby object-model


【解决方案1】:

您可以将原始方法保存为未绑定的方法对象,而不是将其保存在别名中。

然后,您可以将define_method 与块一起使用。该块将在闭包中捕获未绑定的method_object,允许您在新方法中使用它,而不会污染您的模块/类。

唯一的缺点是,您可能无法以这种方式定义产生或获取块的方法:

module Mod
  unbound_method = instance_method(:original_method)
  define_method :original_method do |*args|
    #do something before
    #call the original method
    unbound_method.bind(self).call(*args)
    #do something after
  end
end

【讨论】:

  • 你好,似乎 .bind 方法已在 ruby​​ 2.4.1 中删除,您知道当前的替代方法吗?
【解决方案2】:

我会执行子类化(根据@iain 的回答)或在您自己的实例中包含自定义模块:

module LoggerLiner
  def message(*args)
    puts "="*15
    super
  end
end

log = Logger.new(STDOUT)
log.extend(LoggerLiner)

【讨论】:

  • 可能是在代码中使用依赖注入的最佳方式。谢谢。
【解决方案3】:

这可能不是您所说的“美”,但可能最简单的方法是在您的代码中的某个位置:

class Logger
  alias message old_message
  def message(msg)
    puts "================"
    old_message(msg)
  end
end

【讨论】:

  • alias old_message message,不是alias message old_message
  • 像这样更改公共库对您自己的代码来说很好,但如果要共享您的代码,则“被认为是有害的”。请阅读"The Chainsaw Infanticide Logger Manuever" [原文如此]。
  • 这个 alias_method_chain。嗯,差不多。你的方法用 一个 完全无用的丑陋方法污染了命名空间,而alias_method_chain两个 完全没用的丑陋方法污染了命名空间。
【解决方案4】:

您可以从 logger 继承并使用命名空间来使用它:

class LinedLogger < Logger
  def message(*)
    puts "=" * 15
    super
  end
end

当你想使用它时:

class Post
  Logger = LinedLogger
end

仅在命名空间Post 内,您将获得LinedLogger 而不是Logger。这是限制补丁的好方法。但它不会在全球范围内工作。

【讨论】:

  • 不错。但是你不认为这是我们可以称之为'alias_constant'的一种方式吗?和 alias_method_chain 差不多
猜你喜欢
  • 2016-03-24
  • 2014-04-21
  • 1970-01-01
  • 1970-01-01
  • 2015-05-20
  • 1970-01-01
  • 2018-12-17
  • 2017-05-29
  • 2015-11-17
相关资源
最近更新 更多