给定一个类层次结构如下:
class A
def initialize(param)
if param == 1 then
#initialize and return instance of B
else
#initialize and return instance of C
end
end
end
class B < A
end
class C < A
end
初始化B
时是否可以实际初始化并返回C
或A
的实例?即my_obj = A.new(param)
会导致my_obj
成为类B
或C
的实例,具体取决于param
的值,A.initialize(param)
会检查B
。
在我的用例中,它在运行时只知道使用哪个子类(C
或A
)和父类(B
)基本上从未真正使用过。
我认为将C
或param
决定是否为共同祖先的逻辑可能是一个好主意。
如果这不可能(或样式不好),我应该在哪里检查{{1}}并确定要初始化哪个类?
答案 0 :(得分:11)
你在这里打破了一个基本的OO原则 - 类应该对它们的子类一无所知。当然,有时原则应该被打破,但没有明显的理由在这里做。
更好的解决方案是将实例化逻辑转换为单独类中的工厂方法。工厂方法采用与上面A的初始化程序相同的参数,并返回相应类的实例。
答案 1 :(得分:4)
问题是,initialize
的返回值被忽略。这是当你致电A.new
时会发生什么:
new
调用一个名为allocate
的特殊类方法 - 这将返回一个类的空实例new
然后对initialize
返回的对象调用allocate
,并返回对象要做你想做的事,你需要覆盖new
并让它做你想做的事情:
class A
def self.new(args*)
if(args[0]==1)
B.new(*args[1..-1])
else
C.new(*args[1..-1])
end
end
end
但还有其他事情需要考虑。如果A
从未真正使用过,那么你应该使用某种工厂方法,或者只是一个简单的if语句。例如:
def B_or_C(param,args)
(param == 1 ? B : C).new(args)
end
设计实际上取决于您使用它们的方式。例如,当您有一个可用于处理某些内容的类的类(例如HTML)时,您可以拥有一个主类HTMLParser
,它可以覆盖new
并返回其任何子类:HTML1Parser
,HTML2Parser
,HTML3Parser
,HTML4Parser
和HTML5Parser
。
注意:您必须将new
方法重写为子类中的默认值,以防止无限循环:
def self.new(args*)
obj=allocate
obj.send(:initialize, *args)
obj
end
答案 2 :(得分:0)
您可以创建一个self.instantiate
方法,然后调用.new
,而不是覆盖new。