众所周知,在Ruby中,类方法会被继承:
class P
def self.mm; puts 'abc' end
end
class Q < P; end
Q.mm # works
然而,令我惊讶的是,它不适用于混合物:
module M
def self.mm; puts 'mixin' end
end
class N; include M end
M.mm # works
N.mm # does not work!
我知道#extend方法可以做到这一点:
module X; def mm; puts 'extender' end end
Y = Class.new.extend X
X.mm # works
但我正在编写一个包含实例方法和类方法的mixin(或者更确切地说,我想编写):
module Common
def self.class_method; puts "class method here" end
def instance_method; puts "instance method here" end
end
现在我想做的是:
class A; include Common
# custom part for A
end
class B; include Common
# custom part for B
end
我想要A,B从公共模块继承实例和类方法。当然,这是行不通的。那么,难道没有一种秘密的方法可以让这个继承在单个模块上工作吗?
对我来说,把它分成两个不同的模块似乎很不优雅,一个是包含的,另一个是扩展的。另一个可能的解决方案是使用类Common
而不是模块。但这只是一种解决方法。(如果有两组通用功能Common 1
和Common 2
,我们真的需要混合程序怎么办?)类方法继承不能从混合程序中工作有什么深层原因吗?
正如Sergio在评论中提到的那样,对于已经加入Rails(或者不介意依赖于积极支持)的人来说,关注在这里很有帮助:
require 'active_support/concern'
module Common
extend ActiveSupport::Concern
def instance_method
puts "instance method here"
end
class_methods do
def class_method
puts "class method here"
end
end
end
class A
include Common
end
下面是完整的故事,解释了理解模块包含为什么会像Ruby中那样工作所需的必要元编程概念。
将模块包含到类中会将模块添加到类的祖先。您可以通过调用其祖先
方法来查看任何类或模块的祖先:
module M
def foo; "foo"; end
end
class C
include M
def bar; "bar"; end
end
C.ancestors
#=> [C, M, Object, Kernel, BasicObject]
# ^ look, it's right here!
当您在C
的实例上调用方法时,Ruby会查看此祖先列表的每一项,以便找到具有提供名称的实例方法。由于我们将M
包含在C
中,M
现在是C
的祖先,因此当我们在C
的实例上调用foo
时,Ruby会在M
中找到该方法:
C.new.foo
#=> "foo"
请注意,包含不会将任何实例或类方法复制到类中——它只是向类添加了一个“注释”,即它还应该在包含的模块中查找实例方法。
因为包含只会改变实例方法的调度方式,所以将模块包含到类中只会使其实例方法在该类上可用。模块中的“类”方法和其他声明不会自动复制到类中:
module M
def instance_method
"foo"
end
def self.class_method
"bar"
end
end
class C
include M
end
M.class_method
#=> "bar"
C.new.instance_method
#=> "foo"
C.class_method
#=> NoMethodError: undefined method `class_method' for C:Class
在Ruby中,类和模块是普通对象——它们是类Class
和Module
的实例。这意味着您可以动态创建新类,将它们分配给变量等:
klass = Class.new do
def foo
"foo"
end
end
#=> #<Class:0x2b613d0>
klass.new.foo
#=> "foo"
同样在Ruby中,您可以在对象上定义所谓的单例方法。这些方法作为新实例方法添加到对象的特殊、隐藏的单例类中:
obj = Object.new
# define singleton method
def obj.foo
"foo"
end
# here is our singleton method, on the singleton class of `obj`:
obj.singleton_class.instance_methods(false)
#=> [:foo]
但是类和模块不也是普通的对象吗?事实上它们是!这是否意味着它们也可以有单例方法?是的,确实如此!这就是类方法的诞生方式:
class Abc
end
# define singleton method
def Abc.foo
"foo"
end
Abc.singleton_class.instance_methods(false)
#=> [:foo]
或者,定义类方法的更常见方法是在类定义块中使用Self
,它指的是正在创建的类对象:
class Abc
def self.foo
"foo"
end
end
Abc.singleton_class.instance_methods(false)
#=> [:foo]
正如我们刚刚建立的,类方法实际上只是类对象的单例类上的实例方法。这是否意味着我们可以在单例类中包含一个模块来添加一堆类方法?是的,确实如此!
module M
def new_instance_method; "hi"; end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
self.singleton_class.include M::ClassMethods
end
HostKlass.new_class_method
#=> "hello"
这是自己。singleton_类。include M::ClassMethods行看起来不太好,所以Ruby添加了Object#extend,这也做了同样的事情,即在对象的单例类中包含一个模块:
class HostKlass
include M
extend M::ClassMethods
end
HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
# ^ there it is!
前面的示例不是结构良好的代码,原因有两个:
那么,这样做如何:当我们在第一行调用include时,我们会以某种方式通知模块它已经被包含,并给它我们的类对象,以便它可以调用扩展本身。这样,如果模块愿意,它的工作就是添加类方法。
这正是特殊的自我。包含的方法用于。每当模块被包含到另一个类(或模块)中时,Ruby就会自动调用此方法,并将宿主类对象作为第一个参数传入:
module M
def new_instance_method; "hi"; end
def self.included(base) # `base` is `HostClass` in our case
base.extend ClassMethods
end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
def self.existing_class_method; "cool"; end
end
HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
# ^ still there!
当然,添加类方法并不是我们在self.included
中唯一能做的,我们有class对象,所以我们可以在它上面调用任何其他(类)方法:
def self.included(base) # `base` is `HostClass` in our case
base.existing_class_method
#=> "cool"
end
一个常见的习惯用法是使用包含
挂钩并从那里注入类方法。
module Foo
def self.included base
base.send :include, InstanceMethods
base.extend ClassMethods
end
module InstanceMethods
def bar1
'bar1'
end
end
module ClassMethods
def bar2
'bar2'
end
end
end
class Test
include Foo
end
Test.new.bar1 # => "bar1"
Test.bar2 # => "bar2"
本文向大家介绍Python tkinter模块中类继承的三种方式分析,包括了Python tkinter模块中类继承的三种方式分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Python tkinter模块中类继承的三种方式。分享给大家供大家参考,具体如下: tkinter class继承有三种方式。 提醒注意这几种继承的运行方式 一、继承 object 1.铺tk.Frame给par
类继承 在面向类的语言中,你不仅可以定义一个能够初始化它自己的类,你还可以定义另外一个类 继承 自第一个类。 这第二个类通常被称为“子类”,而第一个类被称为“父类”。这些名词显然来自于亲子关系的比拟,虽然这种比拟有些扭曲,就像你马上要看到的。 当一个家长拥有一个和他有血缘关系的孩子时,家长的遗传性质会被拷贝到孩子身上。明显地,在大多数生物繁殖系统中,双亲都平等地贡献基因进行混合。但是为了这个比拟的
本文向大家介绍JavaScript类的继承方法小结【组合继承分析】,包括了JavaScript类的继承方法小结【组合继承分析】的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了JavaScript类的继承方法。分享给大家供大家参考,具体如下: 在JavaScript 里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)。继承也有之前问题,比如字面量
本文向大家介绍JavaScript中的继承之类继承,包括了JavaScript中的继承之类继承的使用技巧和注意事项,需要的朋友参考一下 继承简介 在JS中继承是一个非常复杂的话题,比其他任何面向对象语言中的继承都复杂得多。在大多数其他面向对象语言中,继承一个类只需使用一个关键字即可。在JS中想要达到继承公用成员的目的,需要采取一系列措施。JS属于原型式继承,得益于这种灵活性,我们既可以
本文向大家介绍Ruby类继承、抽象类、类拓展混入、代理类实例,包括了Ruby类继承、抽象类、类拓展混入、代理类实例的使用技巧和注意事项,需要的朋友参考一下 总结一下工作中遇到的类扩展: 1、类继承: 当多个类公用很多方法的时候可以将公用方法部分抽取出来,需要的类做相关继承。 例子: 2、抽象类 当多个类要继承一个类时,用第一种方法,会遇到一个问题。 (引用一个别人的注解来描述抽象类的运用吧http
问题内容: 在PHP / Java中,可以做到: 并且,Super类的所有公共/受保护的方法,属性,字段等都会自动成为Sub类的一部分,如有必要,可以重写这些类。 Javascript中的等效功能是什么? 问题答案: 我已经更改了现在的操作方式,我尝试避免使用构造函数和它们的属性,但是我从2010年起的旧答案仍然是最底层的。我现在更喜欢。适用于所有现代浏览器。 我应该注意,这通常比使用函数构造函数