setter方法和虚方法(在Ruby中)之间有什么区别?

glo*_*esk 3 ruby setter virtual-functions terminology

我正在研究Ruby并试图绕过一些词汇.在某个地方,我提到了一个概念,即setter概念也是一种虚拟方法,即它们是同义词.但现在我觉得我错了.我想知道这些与工厂方法有什么关系.救命?

维基百科说这关于虚拟方法:

在面向对象的编程中,在诸如C++的语言中,虚函数或虚方法是一种可继承和可覆盖的函数或方法,其促进了动态调度.

自去年六月以来,我一直在努力学习Ruby,我不知道这意味着什么.

我对setter方法有一个更好的概念.我一直在想它只是设置实例变量值的任何方法.所以attr_writer :foo是一个setter方法,也许一个改变值的类外部的方法foo也可能是一个setter方法.是对的吗?

但这不是"虚拟方法"的意思,是吗?所以基本上,我正在寻找差异的解释,我找不到任何(或者,我不能理解).

所谓的"工厂方法"也可以被描述为从类外部创建特定类型的对象(由setter方法集合指定)的方法(即,定义类的代码)?

Ale*_*kin 5

虚方法

方法,在类上定义,并在其子类上重新定义,以便调度与接收器的类型相关.例:

class A; def m; puts "A"; end; end
class B < A; def m; puts "B"; end; end
class C < A; end
[A, B, C].map(&:new).each(&:m)
#? "A"
#  "B"
#  "A"
Run Code Online (Sandbox Code Playgroud)

也就是说,无论一个人是否拥有一个对象,她都不应该在她确定的时候进行类型检查,该对象是任何类的实例,派生自A.如果未在此特定类上定义该方法,则将查找整个继承树,直到找到方法定义并且正在调用找到的方法.

二传法

首先,attr_writer绝不是制定者.它是动态生成setter助手.因此,setter本身就是一个设置[变量]的方法.相反的是吸气剂.

class A; def set(v); @v = v; end; def get; @v; end; end
instance = A.new
instance.set(42)
instance.get
#? 42
Run Code Online (Sandbox Code Playgroud)

工厂方法

它[通常]生成实例.工厂可能在类定义中声明(广泛用于单身人士等):

class A
  def m; puts "A"; end
  # note A.produce, to make it a class method
  def A.produce; A.new; end
  # or self.produce, to make it a class method
  # def self.produce; A.new; end # ? the same as above
end
A.produce
#? #<A:××××××>
A.produce.m
#? "A"
Run Code Online (Sandbox Code Playgroud)

或者在类定义之外:

class A; def m; puts "A"; end; end
def produceA; A.new; end
# or from withing other class
class B; def produceA; A.new; end; end

produceA.m
#? 42
B.new.produceA.m
#? 42
Run Code Online (Sandbox Code Playgroud)

另请查看attr_writer下面Cary Swoveland 的非常有价值的评论.


Jör*_*tag 5

在某个地方,我提到了一个概念,即setter概念也是一种虚拟方法,即它们是同义词.

这是一个逻辑错误:狗也是一个哺乳动物,但这并不意味着它们是同义词.

同样,在Ruby中,setter方法也是虚方法(因为在Ruby中所有方法都是虚方法),但它们不是同义词.由于Ruby中只有虚拟方法,你也可以说:setter方法也是方法.现在,显而易见的是,这并不一定意味着方法也是setter方法,对吗?

维基百科说这关于虚拟方法:

在面向对象的编程中,在诸如C++的语言中,虚函数或虚方法是一种可继承和可覆盖的函数或方法,其促进了动态调度.

这个术语在Ruby中没有意义,因为在Ruby中,所有方法都是虚拟的,因此不需要区分虚拟方法和非虚方法.

在OOP中,术语"虚拟"适用于语言 - 动态调度(即在运行时)并且可以被覆盖的"事物".

class Foo
  def to_s
    foo
  end

  def foo
    'Foo'
  end
end

class Bar < Foo
  def foo
    'Bar'
  end
end

Bar.new.to_s
#=> 'Bar'
Run Code Online (Sandbox Code Playgroud)

如您所见,Bar.new.to_s返回字符串'Bar',即使to_s已定义Foo并简单调用foo.然而,尽管to_s被定义在Foo,它不叫Foofoo,它调用Barfoo,因为所涉及的对象有类Bar.Bar已经用自己的定义覆盖了定义foo,并且调用被动态地调度到当前对象具有的任何类.

创造了"面向对象"一词的Alan Kay使用了一个消息传递隐喻,即恕我直言使这样的事情更容易理解:对象通过发送消息相互通信.它就像在现实世界中向某人发送消息一样:你无法知道接收者对消息做了什么,你所能观察到的就是你得到的回应.当您向某人发送消息时,他们将根据自己的知识解释消息中的请求.

所以,如果你想象你和你朋友之间的这种交流:

  1. 您将消息"将自己转换为字符串"发送给朋友.
  2. 你的朋友不知道这意味着什么,所以他问他的上级,他告诉他,这意味着"给自己发信息'foo'".
  3. 你的朋友给自己发了一条消息"foo".
  4. 你的朋友对"foo"的意思有自己的想法,所以他不需要查阅.

其他语言有其他虚拟"东西",例如Newspeak有虚拟超类.

所以,如果我有这个:

class Foo < Array
  # … stuff
end

class Bar
  def Array
    return SomeClassLikeArray
  end

  def bar
    Foo.new
  end
end

Bar.new.bar
# this will be a `Foo` which has `SomeClassLikeArray` as its superclass
Run Code Online (Sandbox Code Playgroud)

我对setter方法有一个更好的概念.我一直在想它只是设置实例变量值的任何方法.

是的,不是.

这是一种似乎设置实例变量的方法.您实际上并不知道该方法的作用.(记住消息传递的比喻:你只能观察你朋友的回答,你不知道你的朋友对这个消息做了什么!)

例如,在Web框架中,setter方法实际上可以写入数据库而不是设置实例变量.

更一般地说,在Ruby中,setter方法是一个名称以其结尾的方法=.

attr_writer :foo是一个setter方法,

不,那不是一个setter方法.创建一个名为的setter方法foo=.

也许改变值的类外部的方法foo也可能是一个setter方法.是对的吗?

这不是我们通常所说的setter方法.它在Ruby中也是不可能的,因为只有对象本身才能访问其实例变量.

即使在允许它的语言中,也是糟糕的设计:对象应该东西,而不是存储东西.这是关于行为.您应该告诉对象执行操作.

但这不是"虚拟方法"的意思,是吗?所以基本上,我正在寻找差异的解释,我找不到任何(或者,我不能理解).

由于这两个概念是完全正交的,所以谈论它们的差异并没有多大意义; 他们没有任何关系.

虚方法是一种可以被覆盖的方法.setter方法是一种设置东西的方法.您可以使用可以覆盖的setter方法,无法覆盖的setter方法,可以覆盖的非setter方法以及无法覆盖的非setter方法.

特别是在Ruby中,所有方法都是虚拟的,因此所有setter方法都是虚拟的(因为所有setter方法都是方法),但就是这样.

所谓的"工厂方法"也可以被描述为从类外部创建特定类型的对象(由setter方法集合指定)的方法(即,定义类的代码)?

因此,有一个名为Factory Method设计模式,但您正在讨论创建对象的方法的更一般概念.

是的,创建对象的方法有时称为"工厂方法".在Ruby中,最广泛使用的工厂方法是new,它看起来像这样:

class Class
  def new(*args, &block)
    obj = allocate
    obj.initialize(*args, &block)
    return obj
  end
end
Run Code Online (Sandbox Code Playgroud)

实际上,initialize是一个私有方法,所以我们需要使用反射来绕过访问保护,但这并没有改变方法的要点:

class Class
  def new(*args, &block)
    obj = allocate
    obj.__send__(:initialize, *args, &block)
    return obj
  end
end
Run Code Online (Sandbox Code Playgroud)