1

我试图找出一种方法,通过创建一个新的 Listener 类来让一个方法触发另一个方法。我真的很想简化代码,并且不涉及添加任何特定于回调方法或触发方法的内容。基本上,我想做的是:

def level_up
  level += 1
end

def print_level
  puts "Level Up! (#{level})"
end

notify_level = Listener.new(:level_up, :print_level);

我的 Listener 类(现在)是这样的:

# Listener.new(attached_to, callbacks)
class Listener
  def initialize(attached_to, function)
    @owner, @callback = attached_to, function
  end

  def owner
    @owner
  end

  def callback
    @callback
  end

  def trigger
    # execute callback manually
    self.method(@owner).call
    self.method(@callback).call
  end
end

为了调用两者,我需要执行notify_level.trigger自己,但我想要的是执行level_up和调用print_level. 我知道有人会提到一些关于观察者的事情,但我需要的不仅仅是这些。我想坚持干。为每个方法手动添加观察者和侦听器真是太糟糕了,尤其是因为我无法轻松添加或删除它们。

4

2 回答 2

1

就我个人而言,我不是这种模式的忠实粉丝,但这是一个有趣的问题,所以这是我的解决方案。应该在 Ruby 1.9 及更高版本中工作。

module MethodListener
  @@observed_methods = {}

  def method_added(method)
    alias_name = "__#{method}_orig"
    return if method_defined?(alias_name) || method.match(/__.*_orig/)

    alias_method alias_name, method
    define_method(method) do |*args|
      ret = send(alias_name, *args)
      (@@observed_methods[method] || []).each {|callback| send(callback)}
      ret
    end
  end

  def listen(owner, callback)
    (@@observed_methods[owner] ||= []) << callback
  end
end

使用示例:

class A
  extend MethodListener

  def b(a,b)
    puts "b #{a} #{b}"
    true
  end

  def c
    puts 'c'
  end

  listen :b, :c
end

A.new.b(1,2) # => true
# Prints:
# b 1 2
# c
于 2013-08-29T06:20:00.550 回答
0

我将原始代码更改为更具语义性,因此更有意义。

class Event
  def initialize(event, callback_array = [])
    if callback_array.kind_of? Array
      @callbacks = callback_array
    else
      @callbacks = [callback_array]
    end

    @event = event
  end

  def trigger(*args)

    self.method(@event).call *args

    @callbacks.each{ |callback|
      if callback.instance_of? Event
        callback.trigger *args
      else
        method(callback).call *args
      end
    }
  end

  def add(callback)
    @callbacks.push callback
  end

  def remove(callback)
    @callbacks.delete_at(@callbacks.index(callback) || @callbacks.length)
  end

  def event_name
    @event
  end
end

用法:

$infinite_break = 10

def infinite_loop_a(type)
  puts "#{$infinite_break} points of #{type} damage taken"
  $infinite_break -= 1

  if $infinite_break > 0
    $infinite.trigger(type)
  else
    $infinite.remove(:infinite_loop_a)
  end
end

def infinite_loop_b(type)
  puts "player is dealing #{$infinite_break} damage"
end

$infinite = Event.new(:infinite_loop_b, :infinite_loop_a)
$infinite.trigger('fire')

另外,我知道我在打电话给infinite_loop_binside infinite_loop_a,但这是出于特定原因。Event实例可以有另一个作为Event回调。

于 2013-08-29T22:20:59.667 回答