“个人”红宝石中的方法

发布于 2024-08-27 06:53:01 字数 383 浏览 12 评论 0 原文

我正在寻找一种使方法成为“个人”的方法 - 注意

这里是一个类的非私有示例 - 通过“个人”我的意思是方法“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

我正在寻找一种产生以下内容的方法行为

a.foo #=> "foo"

b.foo #=> "bar"

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

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

彡翼 2024-09-03 06:53:01

不要创建“个人”方法,而是更改继承结构。

看来您希望 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

Instead of creating 'personal' methods, change your inheritance structure.

It appears that you want the C class to have only some of the same functionality of the B class while not making changes to the A class.

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
蝶舞 2024-09-03 06:53:01

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

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

您当然可以在 Class 上定义一个方法,该方法将为您执行类似于 publicprivate 的操作。

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

然后您可以在 B 中使用 personal :foo,就像 private :foo 一样。

(这根本没有优化,而且我没有实现 publicprivate 所具有的零参数行为,因为坦率地说,要做正确的事情是一个巨大的 PITA,即使这样这是一个黑客行为。)

There's no standard way of doing this. It circumvents how inheritance works. You could implement B's method to do the logic like this:

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

And you could of course define a method on Class that would do this for you similar to public and 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

Then you can personal :foo in B just like you'd private :foo.

(This isn't at all optimized and I didn't implement the zero-argument behavior that public and private have because frankly it's a huge PITA to do right and even then it's a hack.)

薄荷→糖丶微凉 2024-09-03 06:53:01

看起来这可能会令人困惑,但这里有一个选择:

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

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

如果您向我们提供您最终想要实现的目标的具体信息,我们可能会更有帮助。

Seems like it could be confusing, but here's one option:

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

More generally, using a similar approach, you can always add a method to a given instance, which of course has no effect on inherited classes or indeed on other instances of the same class.

If you give us specifics of what you're ultimately trying to accomplish we can probably be more helpful.

空城缀染半城烟沙 2024-09-03 06:53:01

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

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

ancestors[-3] 假设 A 继承自 Object 和 Kernel,并且您的目的是从最顶层的非内置类访问该方法,当然您可以用 A 替换 self.class.ancestors[-3] ,或者弄清楚。 :

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

Answering this is a bit tricky since I don't really see what you want to accomplish in practice, but you could try something like

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

(The ancestors[-3] assumes that A inherits from Object and Kernel and your intent was to access the method from the topmost non-builtin class. Of course you could substitute self.class.ancestors[-3] with just A, or figure out the class from the Array ancestors yourself, etc.)

In practice it would be simpler to alias the original in class B if you can modify it (i.e. alias :foo_from_A :foo in class B < A before the new def foo, then you can call foo_from_A in C). Or just redefine what you want in C. Or design the whole class hierarchy differently.

不甘平庸 2024-09-03 06:53:01

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

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

You can write a shortcut function to handle personalizing methods.

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
尐偏执 2024-09-03 06:53:01

有时您并不真正想要“是”(继承)关系。有时你想要的是“像嘎嘎一样”。通过使用模块“混合”方法,可以轻松地在“庸医”类之间共享代码:

#!/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"

Sometimes you don't really want an "is a" (inheritance) relationship. Sometimes what you want is "quacks like a." Sharing code among "quacks like a" classes is easily done by using modules to "mix in" methods:

#!/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"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文