comida - Bueno o malo OOP?
design-patterns architecture (4)
Supongamos que hay una clase abstracta con un constructor que llama a un método abstracto protegido que todavía no ha sido implementado por la clase hija. ¿Es esta una buena o mala idea? ¿Por qué?
Esta es una mala idea, porque en la mayoría de los lenguajes OOP, el niño se inicializa después de que el padre se inicializa. Si la función abstracta se implementa en el niño, entonces puede operar en los datos en el niño bajo la suposición incorrecta de que ya se ha inicializado, lo que no es el caso en la construcción del padre.
Ejemplo:
class Base {
public:
Base() { virtualInit(); }
virtual ~Base() {}
protected:
virtual void virtualInit() {}
};
class Derived : public Base {
public:
Derived() : ptr_(new SomeObject) {}
virtual ~Derived() {}
protected:
virtual void virtualInit() {
// dereference ptr_
}
private:
scoped_ptr<SomeObject> ptr_;
};
En el ejemplo anterior, Base::Base()
se ejecuta antes Derived::Derived()
, que es responsable de inicializar ptr_
. Por lo tanto, cuando se llama a virtualInit()
desde Base::Base()
se desreferencia un puntero no inicializado, lo que genera todo tipo de problemas. Esta es la razón por la que los ctors y dtors deben llamar solo a funciones no virtuales (en C ++), funciones finales (en Java) o el equivalente específico del idioma.
Esta es una mala idea.
Básicamente estás creando inversión de control dentro de un constructor. El método en la clase base que se llama se llama antes de que se inicialicen los datos de la clase base (en la mayoría de los lenguajes), lo que también es peligroso. Puede conducir fácilmente a un comportamiento indeterminado.
Recuerde, en la mayoría de los lenguajes, cuando construye una clase, toda la construcción de la clase base se ejecuta primero. Entonces, si tiene algo como: MyClass() : MyBaseClass() {}
, típicamente, el constructor de MyBaseClass
ejecuta en su totalidad, entonces el constructor de MyClass
ejecuta. Sin embargo, al usar un método virtual en la clase base, está llamando a un método de instancia en MyClass
antes de que esté completamente inicializado, lo que podría ser muy peligroso.
No puedo ver el razonamiento de por qué querrías hacer esto, mucho menos calificarlo. Parece que estás intentando inyectar alguna funcionalidad en el objeto. ¿Por qué no simplemente sobrecargar el constructor o crear una propiedad que se puede establecer para que pueda inyectar la funcionalidad a través de la composición o incluso crear el constructor con un parámetro que sea el objeto IOC?
Como otro ya ha publicado, depende del contexto en el que trate de resolver un problema en particular. El ajuste natural sería adherirse a una interfaz, desarrollar una clase abstracta y sobrecargar al constructor en cada implementación. Sin más información, solo puedo comentar lo que se ha publicado en su pregunta.
Este diseño que tienes no puede ser considerado bueno o malo. Simplemente por el hecho de que puede ser la única forma en que puede lograr lo que intenta hacer.
Solo entonces es una BUENA idea, IFF puedes lograr que no dispare con tu propio pie. Pero de todos modos, OOP siempre es propenso a diseños malos; entonces, si su lenguaje permite otros paradigmas además de OOP, usar OOP en este caso es definitivamente una MALA elección.
Al menos en C ++, la clase hija define en qué secuencia se realizan todas las inicializaciones; que son las inicializaciones de todas las variables miembro y los constructores de todas las clases padre. Esto debe ser considerado!
Alternativamente, podría darle al constructor (como parámetro) un puntero a una función específica (preferiría funciones estáticas), en lugar de llamar a una función de miembro abstracta. Esta podría ser una solución mucho más elegante y sensata; y esta es la solución habitual en (probablemente todos) los lenguajes no OOP. (en java: un puntero a una función o una lista de funciones es el patrón de diseño de una interfaz y viceversa).