4

I'm looking for a way of making a method "personal" - note NOT PRIVATE to a class

here is an example - by "personal" I mean the behaviour of method "foo"

class A
  def foo
     "foo"
  end
end

class B < A
  def foo
      "bar"
  end
end

class C < B
end

a=A.new; b=B.new;c=C.new

I'm looking for a way of producing the following behaviour

a.foo #=> "foo"

b.foo #=> "bar"

c.foo #=> "foo" (ultimate base class method called)
4

6 回答 6

3

与其创建“个人”方法,不如改变你的继承结构。

看来您希望 C 类仅具有 B 类的一些相同功能,而不对 A 类进行更改。

class A
  def foo
     "foo"
  end
end

class BnC < A
end

class B < BnC
  def foo
      "bar"
  end
end

class C < BnC
end

a=A.new; b=B.new;c=C.new
于 2010-03-31T20:43:59.893 回答
2

没有标准的方法可以做到这一点。它规避了继承的工作方式。您可以实现 B 的方法来执行如下逻辑:

def foo
  instance_of?(B) ? "bar" : super
end

你当然可以在 Class 上定义一个方法来为你做这件事,类似于publicand private

class Class
  def personal(*syms)
    special_class = self
    syms.each do |sym|
      orig = instance_method(sym)
      define_method(sym) {|*args| instance_of?(special_class) ? orig.bind(self).call(*args) : super}
    end
  end
end

然后你可以personal :foo像你一样在 B 中private :foo

(这根本没有优化,我没有实现零参数行为,public因为private坦率地说,这是一个巨大的 PITA 做对了,即使那样它也是一个 hack。)

于 2010-03-31T20:17:07.753 回答
2

似乎它可能会令人困惑,但这里有一个选项:

class A
  def foo
     "foo"
  end
end

class B < A
 def initialize #when constructing, add the new foo method to each instance
    def self.foo
      "bar"
    end 
 end
end

class C < B
 def initialize #when constructing, do nothing
 end
end

更一般地说,使用类似的方法,您始终可以向给定实例添加方法,这当然对继承的类或同一类的其他实例没有影响。

如果您向我们详细说明您最终要完成的工作,我们可能会更有帮助。

于 2010-03-31T20:55:06.720 回答
0

您可以编写一个快捷函数来处理个性化方法。

def personalize(*methodNames)
  old_init = instance_method(:initialize)
  klass = self
  modul = Module.new {
    methodNames.each { |m|
      define_method(m, klass.instance_method(m)) if klass.method_defined?(m)
    }
  }
  methodNames.each { |m| 
    remove_method(m) if method_defined?(m)
  }
  define_method(:initialize) { |*args|
    # I don't like having to check instance_of?, but this is the only way I 
    # could thing of preventing the extension of child classes. At least it only
    # has to happen once, during initialization.
    extend modul if instance_of?(klass)
    old_init.bind(self).call(*args)
  }
  self
end

class A
  def foo
     "foo"
  end
end

class B < A
  def foo
      "bar"
  end
  def bam
    'bug-AWWK!'
  end
  personalize :foo, :bam, :nometh
end

class C < B
end

a=A.new; b=B.new; c=C.new
a.foo #=> "foo"
b.foo #=> "bar"
b.bam #=> "bug-AWWK!"
c.foo #=> "foo"
C.instance_method(:foo) # => #<UnboundMethod: C(A)#foo>
c.bam #throws NoMethodError
于 2010-03-31T23:22:27.353 回答
0

有时您并不真正想要“是”(继承)关系。有时你想要的是“嘎嘎声”。通过使用模块来“混合”方法,可以很容易地在“quacks like a”类之间共享代码:

#!/usr/bin/ruby1.8

module BasicFoo
  def foo
     "foo"
  end
end

class A
  include BasicFoo
end

class B
  def foo
      "bar"
  end
end

class C
  include BasicFoo
end

p A.new.foo    # => "foo"
p B.new.foo    # => "bar"
p C.new.foo    # => "foo"
于 2010-04-01T20:30:53.167 回答
0

回答这个问题有点棘手,因为我真的不知道你想在实践中完成什么,但你可以尝试类似

class C < B
  def foo
    self.class.ancestors[-3].instance_method(:foo).bind(self).call
  end
end

ancestors[-3]假设 A 继承自 Object 和 Kernel 并且您的意图是从最顶层的非内置类访问该方法。当然您可以self.class.ancestors[-3]用 just替换A,或者自己从 Array 中找出类ancestors等)

B在实践中,如果您可以修改原始类中的别名(即alias :foo_from_A :fooclass B < Anew 之前def foo,然后您可以调用foo_from_Ain C) ,那么在类中为原始别名会更简单。或者只是重新定义你想要的C。或者以不同的方式设计整个类层次结构。

于 2010-03-31T20:54:31.930 回答