如题,一个类继承另一个类,如何取消继承关系,别告诉我把“<”符号删掉,如果我想取消继承 Object 类呢?
问题不是关注点,我觉得 这种思维方式 和 讨论的热情是关注点。
#11 楼 @fsword #12 楼 @hooopo #13 楼 @jasl 让一个类直接继承 BasicObject 是有应用场景的,尤其是在写一个 Builder-Style 的 DSL 时,我们需要一个比较干净的基类,以免对 Builder 的行为造成干扰。
拿 Builder 这个 gem 来说明一下:
module Builder
class XmlBase < BlankSlate
......
end
end
module Builder
if Object::const_defined?(:BasicObject)
BlankSlate = ::BasicObject
else
require 'blankslate'
BlankSlate = ::BlankSlate
end
end
其实,BlankSlate 这个 gem 就是从 Builder 中剖离了出来的,并单独做成了一个 gem。
就像最开始说的那样.. 怎样能取消一个已有类的继承关系?或者 mixin module 的关系..
现在看来,大家的解决方案都是 undef 掉 mixin method, 或者 undef 掉 继承的 method.
但是实际上没有解决本质问题。
实际上这个在 Java 里面挺好解决的。Java 可以把老的类从 classloader 里面清理掉。重新 load 一个新的.. Ruby 没有 classloader 机制。貌似 cache 是不清理的。
大家可以发散一下。
[0] pry(main)> Array.included_modules
=> [Enumerable, PP::ObjectMixin, Kernel]
没解决这里面的问题。
A.included_modules
=> [Enumerable, PP::ObjectMixin, Kernel]
ClassLoader.load(A.class)
=> [A.class]
ClassLoader.unload(A.class)
=> []
File.edit(A.class)
=> ooxx
ClassLoader.load(A.class)
=> [PP::ObjectMixin, Kernel]
就像这样直接替换掉啊
>>> class A(object):
... a = 1
...
>>> class B(A):
... pass
...
>>> b = B()
>>> b.a
1
>>> class B(object):
... pass
...
>>> b.__class__ = B
>>> b.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'B' object has no attribute 'a'
[1] pry(main)> class A
[1] pry(main)* end
=> nil
[2] pry(main)> class B < A
[2] pry(main)* end
=> nil
[3] pry(main)> B.superclass
=> A
[4] pry(main)> class B < Object
[4] pry(main)* end
=> nil
[5] pry(main)> B.superclass
=> A
[6] pry(main)> class B < BasicObject
[6] pry(main)* end
TypeError: superclass mismatch for class B
from (pry):9:in `<main>'
@saito 如果是实现白板类,那么 classloader 很有用,它就是搞了一个 new world,所以很干净
不过如果仅仅是取消继承类,那么 classloader 解决不了这个问题,比如你希望的是类 X 不再继承类 Y,那么我认为你的目的是让所有用到 X 的地方都 assert 下面的表达式
X.new.is_a? Y == false
然而 java 的 cl 不是这个意思,它是 build 了一个 new world,在这里面 Y 不是 X 的父类(原来的那个 world 里,Y 依然是 X 的父类)
换个角度看,classloader 机制导致的结果是,我们不能通过类名来识别一个类,而是要用 classloader + 类名组合来识别,在这个意义上,我们不能改变类的继承关系(只能创建新的类——用 cl+ 类名标识)。所以我一直认为 classloader 好处有限,麻烦多多
>>> class A:
... pass
...
>>> class B(A):
... pass
...
>>> B.__bases__
(<class __main__.A at 0x10a7fdce8>,)
>>> class B(object):
... pass
...
>>> B.__bases__
(<type 'object'>,)
[1] pry(main)> class A
[1] pry(main)* end
=> nil
[2] pry(main)> class B < A
[2] pry(main)* end
=> nil
[3] pry(main)> B.superclass
=> A
[4] pry(main)> B = Class.new
(pry):6: warning: already initialized constant B
=> B
[5] pry(main)> class B < Object
[5] pry(main)* end
=> nil
[6] pry(main)> B.superclass
=> Object
[7] pry(main)> b = B.new
=> #<B:0x007ff5b192e6a0>
[8] pry(main)> b.class
=> B
[10] pry(main)> def b.class
[10] pry(main)* "C"
[10] pry(main)* end
=> nil
[11] pry(main)> b.class
=> "C"
这个作弊太方便了。
[1] pry(main)> class A
[1] pry(main)* def a;end
[1] pry(main)* end
=> nil
[2] pry(main)> class B < A
[2] pry(main)* end
=> nil
[3] pry(main)> B.new.methods - Object.new.methods
=> [:a]
[4] pry(main)> class C
[4] pry(main)* def c; end
[4] pry(main)* end
=> nil
[5] pry(main)> B = Class.new C
(pry):10: warning: already initialized constant B
=> B
[6] pry(main)> B.new.methods - Object.new.methods
=> [:c]
instance 需要你主动去替换的,换掉 instance 的 class 之后,旧版的 class 就可以被回收了
>>> class A(object):
... pass
...
>>> a = A()
>>> class B(object):
... pass
...
>>> a.__class__ = B
>>> class A(object):
... pass
...
>>> old = a = A()
>>> class B(object):
... pass
...
>>> a.__class__ = B
>>> a == old
True
>>> a = B()
>>> a == old
False
直接改 __class__
,引用 a 的地方,你不用去改。生成一个新的 object,你得改所有引用旧的 object 的地方,这个好像没啥办法。