如何在初始化父类时返回子类的新实例?

时间:2012-06-17 19:29:03

标签: ruby oop class inheritance

给定一个类层次结构如下:

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时是否可以实际初始化并返回CA的实例?即my_obj = A.new(param)会导致my_obj成为类BC的实例,具体取决于param的值,A.initialize(param)会检查B

在我的用例中,它在运行时只知道使用哪个子类(CA)和父类(B)基本上从未真正使用过。 我认为将Cparam决定是否为共同祖先的逻辑可能是一个好主意。

如果这不可能(或样式不好),我应该在哪里检查{{1}}并确定要初始化哪个类?

3 个答案:

答案 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并返回其任何子类:HTML1ParserHTML2ParserHTML3ParserHTML4ParserHTML5Parser

注意:您必须将new方法重写为子类中的默认值,以防止无限循环:

def self.new(args*)
  obj=allocate
  obj.send(:initialize, *args)
  obj
end

答案 2 :(得分:0)

您可以创建一个self.instantiate方法,然后调用.new,而不是覆盖new。