2012-06-17 11 views
9

dado una jerarquía de clases de la siguiente manera:¿Cómo devolver una nueva instancia de la subclase al inicializar la clase padre?

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 

¿Es posible inicializar y devolver una instancia de B o C realidad al inicializar A? Es decir. my_obj = A.new(param) daría como resultado my_obj siendo una instancia de la clase B o C dependiendo del valor de param, que se comprueba en A.initialize(param).

En mi usecase solo se conoce en tiempo de ejecución qué subclase (B o C) usar y la clase principal (A) básicamente nunca se usa realmente. Pensé que sería una buena idea mover la lógica de decidir si B o C en su ancestro común.

Si esto no es posible (o un estilo incorrecto), ¿dónde debo poner el cheque de param y la decisión de qué clase inicializar?

+1

Preguntas similares han sido respondidas antes.Lo que probablemente estés buscando son métodos de fábrica. Eche un vistazo a http://stackoverflow.com/questions/1515577/factory-methods-in-ruby Supongo que la respuesta en http://stackoverflow.com/a/1515580/1128705 se ajustará a sus necesidades. –

Respuesta

8

Estás rompiendo un principio fundamental OO aquí - clases deben saber nada acerca de su subclases. Por supuesto, a veces los principios deberían romperse, pero no hay ninguna razón aparente para hacerlo aquí.

Una solución mucho mejor es cambiar la lógica de creación de instancias a un método de fábrica en una clase separada. El método de fábrica toma los mismos argumentos que el inicializador de A arriba y devuelve una instancia de la clase apropiada.

+0

Gracias por decirlo en voz alta. :) Finalmente escribí un método 'initiate_A' en un módulo separado, que toma la decisión y devuelve un nuevo objeto. –

+0

Puede haber uno o dos casos en los que esto sea apropiado. Como dije en mi respuesta, ¿qué pasa si tienes una clase de analizador, que tiene que tratar, por ejemplo, con diferentes versiones de un idioma? Podría tener un constructor que devuelva una instancia de la subclase correcta para la versión correcta de los datos. HTML5 es HTML. No es una buena relación de herencia, no sé lo que es. – Linuxios

+0

Estamos de acuerdo el uno con el otro: este principio se puede romper cuando hay una buena razón para hacerlo. Aunque no estoy seguro de que me guste tu ejemplo, suena más como un caso para Abstract Factory. – ComDubh

3

El problema es que se ignora el valor de retorno de initialize. esto es lo que ocurre cuando se llama A.new:

  • new llama a un método de clase especial llamada allocate - esto devuelve una instancia vacía de la clase
  • new llama entonces initialize en el objeto devuelto por allocate, y devuelve el objeto

para hacer lo que quiere hacer, se debe redefinir new y hacer que haga lo que quiere:

class A 
    def self.new(args*) 
    if(args[0]==1) 
     B.new(*args[1..-1]) 
    else 
     C.new(*args[1..-1]) 
    end 
    end 
end 

Sin embargo, hay algo más que considerar. Si A nunca se usa realmente por sí solo, debe usar algún tipo de método de fábrica, o simplemente una simple declaración if. Por ejemplo:

def B_or_C(param,args) 
    (param == 1 ? B : C).new(args) 
end 

El diseño realmente depende de para qué los utilice. Cuando, por ejemplo, tiene una clase que podría usarse para manejar varias versiones de algo, por ejemplo, HTML, podría tener una clase principal HTMLParser que sustituya new y podría devolver cualquiera de sus subclases: HTML1Parser, HTML2Parser, HTML3Parser, HTML4Parser y HTML5Parser.

Nota: Hay que reemplazar el método new a la predeterminada en las sub-clases para evitar bucles infinitos:

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

Gracias por su respuesta. Sin embargo, al anular 'self.new' en' A' y llamar 'A.new (params)', aparece un error: 'SystemStackError: stack level too deep'. Parece que llamar 'B.new' (o' C.new') dentro de 'A.new' causa un bucle infinito ya que los" constructores "de' B' y 'C' de alguna manera están llamando al" constructor "de 'A', es decir' A.new'. –

+0

Debido a la herencia –

+0

@Torbjoern: Vea Editar. – Linuxios

Cuestiones relacionadas