2010-11-03 20 views
9

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 secundaria. ¿Es esta una buena o mala idea? ¿Por qué?¿Bien o mal OOP?

+3

Sí. las clases abstractas fueron una mala idea. – tidwall

+0

Las clases abstractas no son una mala idea, solo la posibilidad de llamar a métodos virtuales desde un constructor. La idea también es mala para clases concretas, no solo abstractas;) –

+0

@Reed, no, las clases abstractas son una mala idea (aunque por otras razones). Normalmente, la delegación/agregación es un mecanismo mejor y menos propenso a errores para la reutilización de código que la herencia. La herencia de interfaz está bien (es decir, no hay implementación), pero la herencia de clase fue una idea terrible. –

Respuesta

3

Esta es una mala idea.

Básicamente está 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. Por lo tanto, si tiene algo como: MyClass() : MyBaseClass() {}, normalmente, el constructor de MyBaseClass se ejecuta en su totalidad, y luegoMyClass se ejecuta el constructor. 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.

+0

Buen punto. Gracias por tu visión. – StackOverflowNewbie

2

Esto es una mala idea, porque en la mayoría de los lenguajes de 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() es ejecutado antes de Derived::Derived(), que es responsable de la inicialización ptr_. Por lo tanto, cuando se llama al virtualInit() desde Base::Base(), 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.

1

No puedo ver el razonamiento de por qué quieres hacer esto, y 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 ya se 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 tiene no se puede considerar bueno o malo. Simplemente por el hecho de que puede ser la única forma en que puede lograr lo que intenta hacer.

+0

Gracias por la entrada. Realmente no estoy tratando de resolver un problema en particular. Solo solicitando opiniones para una posible solución de diseño. – StackOverflowNewbie

+0

¿cómo son mejores las interfaces? ¿Qué sucede si necesito una clase común para heredar de todas las clases? La clase común en sí misma no se debe instanciar directamente, pero contiene una cantidad de métodos y propiedades que las clases descendientes necesitan. ¿No necesitarías una clase abstracta para que puedas beneficiarte de la herencia? – StackOverflowNewbie

+0

Preferencia personal, siempre prefiero la composición sobre la herencia en los diseños de mi programa. Encuentro que las soluciones son más robustas. Me gustaría ver el problema utilizando el enfoque CRC y resolver las interacciones más que la herencia. Un objeto tiene una responsabilidad y debería ser muy bueno en eso. También debería poder ser reemplazado por otro de tipo similar. Por lo tanto, primero analizaría los comportamientos y el diseño en torno a estos. No digo que no haría herencia, pero no es algo en lo que piense desde el principio, generalmente ocurre cuando pienso volver a factorizar. – WeNeedAnswers

0

Es solo entonces una BUENA idea, IFF puede lograr que no dispare con su 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 secundaria 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).