我曾经认为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.new.to_s
返回字符串
'Bar'
,即使
to_s
在
Foo
中定义并仅调用
foo
。但是,尽管
to_s
在
Foo
中定义,它不会调用
Foo
的
foo
,而是调用
Bar
的
foo
,因为所涉及的对象具有类
Bar
。
Bar
已经
覆盖了
foo
的定义,并且调用被
动态地分派到当前对象具有的任何类。
Alan Kay创造了“面向对象”这个术语,并使用了一个消息隐喻,我认为这使得像这样的事情更容易理解:对象通过发送消息彼此通信。它的工作方式就像在现实世界中向
某人发送消息一样:您无法知道接收者如何处理消息,您能观察到的只是您获得的响应。当您向某人发送消息时,他们将根据自己的知识解释消息中的请求。
如果你想象一下你和朋友之间的交流:
- 你向朋友发送消息“将自己转换为字符串”。
- 你的朋友不知道这是什么意思,于是他问他的上级,他告诉他,这意味着“向自己发送消息'foo'”。
- 你的朋友向自己发送消息“foo”。
- 你的朋友有自己对“foo”的理解,所以他不需要查找它的含义。
其他语言也有其他虚拟的“东西”,例如Newspeak有虚拟超类。
因此,如果我有这个:
class Foo < Array
end
class Bar
def Array
return SomeClassLikeArray
end
def bar
Foo.new
end
end
Bar.new.bar
我对setter方法有了更好的认识。我一直在想它只是设置实例变量值的任何方法。
是和不是。
它是一个看起来设置实例变量的方法。你并不知道那个方法实际上做了什么。(记住消息传递的比喻:你只能观察到朋友的反应,你不知道朋友实际上用消息做了什么!)
例如,在Web框架中,setter方法可能实际上写入数据库而不是设置实例变量。
更技术性地说,在Ruby中,通常,以
=
结尾的方法是setter方法。
所以
attr_writer :foo
是一个setter方法,
不,那不是一个setter方法。它
创建了一个名为foo=
的setter方法。
也许一个外部于类的方法改变了
foo
的值也是一个setter方法。是这样吗?
这并不是我们通常所说的setter方法。在Ruby中也根本不可能,因为只有对象本身才能访问它的实例变量。
即使在允许的语言中,这也是糟糕的设计:对象应该执行操作而不是存储东西。它关乎行为。你应该告诉对象执行动作。
但这不是“虚拟方法”所指的意思,是吧?所以,基本上,我正在寻找区别的解释,但我找不到任何(或者,我无法理解)。
这两个概念完全不相关,讨论它们的区别并没有什么意义。
虚拟方法是可以被覆盖的方法。Setter方法是设置内容的方法。你可以有一个可以被覆盖的setter方法,一个不能被覆盖的setter方法,一个可以被覆盖的非setter方法和一个不能被覆盖的非setter方法。
特别地,在Ruby中,所有方法都是虚拟的,所以所有的setter方法都是虚拟的(因为所有的setter方法都是方法),但就此而已。
这也是事实,一个所谓的“工厂方法”可以被描述为一种从类的外部(即定义类的代码)使用集合的setter方法创建特定类型对象的方法。因此,有一个名为“工厂方法”的
设计模式,但您正在谈论更一般的创建对象的方法概念。是的,一个创建对象的方法
有时被称为“工厂方法”。在Ruby中,最常用的工厂方法是
new
,
大致如下:
class Class
def new(*args, &block)
obj = allocate
obj.initialize(*args, &block)
return obj
end
end
实际上,initialize
是一个私有方法,因此我们需要使用反射来规避访问保护,但这并不改变该方法的要点:
class Class
def new(*args, &block)
obj = allocate
obj.__send__(:initialize, *args, &block)
return obj
end
end