【问题标题】:How to return new instance of subclass while initializing parent class?如何在初始化父类时返回子类的新实例?
【发布时间】:2012-06-19 21:51:40
【问题描述】:

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

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

在初始化A 时,是否可以实际初始化并返回BC 的实例? IE。 my_obj = A.new(param) 将导致 my_obj 成为 BC 类的实例,具体取决于 param 的值,该值将在 A.initialize(param) 中签入。

在我的用例中,它只在运行时知道要使用哪个子类(BC),而父类(A)基本上从未真正使用过。 我认为将决定是 B 还是 C 的逻辑移到它们的共同祖先中可能是个好主意。

如果这是不可能的(或不好的风格),我应该把param的检查放在哪里并决定初始化哪个类?

【问题讨论】:

标签: ruby oop class inheritance


【解决方案1】:

问题是,initialize 的返回值被忽略了。当您致电 A.new 时会发生以下情况:

  • new 调用一个名为 allocate 的特殊类方法——这将返回该类的一个空实例
  • new然后在allocate返回的对象上调用initialize,并返回该对象

要做你想做的事,你需要覆盖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

【讨论】:

  • 感谢您的回答。但是,当在A 中覆盖self.new 并调用A.new(params) 时,我收到一个错误:SystemStackError: stack level too deep。看来,在A.new 内部调用B.new(或C.new)会导致无限循环,因为BC 的“构造函数”以某种方式调用A 的“构造函数”,即@ 987654351@.
  • @ismaelga:没错。当Bnew方法被调用时,它会沿着继承链向上到达AA再次实例化B,导致无限循环。
【解决方案2】:

你在这里打破了一个基本的 OO 原则——类应该对它们的子类一无所知。当然,有时原则应该被打破,但这里没有明显的理由这样做。

更好的解决方案是将实例化逻辑转移到单独类中的工厂方法。工厂方法采用与上述 A 的初始化程序相同的参数,并返回相应类的实例。

【讨论】:

  • 感谢您大声说出来。 :) 我终于在一个单独的模块中写了一个方法initiate_A,它做决定并返回一个新对象。
  • 可能有一两种情况是合适的。正如我在回答中所说,如果您有一个解析器类,它必须处理例如不同版本的语言怎么办?您可以有一个构造函数,它为正确的数据版本返回正确的子类的实例。 HTML5就是HTML不是很好的继承关系,不知道是什么。
  • 我们彼此同意——如果有充分的理由,这个原则可以被打破。不确定我是否喜欢您的示例,这听起来更像是抽象工厂的案例。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-27
    • 2013-04-14
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    • 1970-01-01
    相关资源
    最近更新 更多