11

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

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或的实例?即将导致成为类的实例或取决于 的值,这将被签入。CAmy_obj = A.new(param)my_objBCparamA.initialize(param)

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

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

4

3 回答 3

12

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

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

于 2012-06-17T23:36:46.583 回答
4

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

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

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

不过,还有其他事情需要考虑。ifA从来没有真正单独使用过,你应该使用某种工厂方法,或者,只是一个简单的 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
于 2012-06-17T19:34:44.440 回答
0

您可以创建一个self.instantiate方法,然后调用.new.

于 2020-06-26T20:43:27.683 回答