2011-05-14 8 views
5

código heredado dado, el sistema tiene la siguiente jerarquía de clases:método de reemplazo para una familia de subclases

  Base 
     ^
      | 
----------+--------------- 
^ ^ ^ ^ ^
|  |  |  |  | 
A1  B1 C1 D1 E1 
^ ^ ^ ^ ^
|  |  |  |  | 
A2  B2 C2 D2 E2 
....... 
^ ^ ^ ^ ^
|  |  |  |  | 
An  Bn Cn Dn En 

La jerarquía representa los mensajes en un dominio específico.

La clase base es, por supuesto, la clase base de todos los mensajes. A1..E1 son mensajes que pertenecen a la versión 1 del dominio, A2..E2 a la versión 2, y así sucesivamente. Tenga en cuenta que An debe heredar directamente de An-1, ya que An anula los métodos específicos de An-1.

Hay alguna funcionalidad que es común a todos los mensajes, por lo que se define como Base :: PerformFunctionality. Alguna parte de la funcionalidad es específica de la versión n, por lo que existe la función virtual Base :: SpecificPartOfFunctionality, que se llama por Base :: PerformFunctionality.

Así que mi problema es cómo anular Base :: SpecificPartOfFunctionality específica por todos An..En.

veo 2 soluciones posibles, que no me gusta demasiado:

  1. Implementar Base :: SpecificPartOfFunctionality en todos y cada uno An..En.

    El problema con esta solución es que la implementación debe ser exactamente igual para cada clase, así que simplemente repito el código.

    El problema adicional es que si se introduce una nueva clase Fn, el desarrollador puede olvidarse de implementar SpecificPartOfFunctionality específica.

  2. Introducir BaseN se derivan de la clase base, mientras que cada uno An..En derivan de BaseN también:

    class BaseN : public Base { 
        //... 
        SpecificPartOfFunctionality() { ...} 
    }; 
    
    class An: public An-1, public BaseN { .. } 
    

    El problema de esta solución es que introduce diamante problema.

    problema adicional es lo que ocurrirá si alguna otra versión m necesita para anular Base :: SpecificPartOfFunctionality también. Siguiendo la solución presentaremos BaseM, que anulará Base :: SpecificPartOfFunctionality. Por lo cual SpecificPartOfFunctionality será llamado para Un - de BaseN o de BaseN. Es un completo desastre.

¿Alguna sugerencia?

+0

¿Se le permite cambiar las clases A1..E1? Si es así, ¿por qué no implementar 'SpecificBase' que implementa' SpecificPartsOfFunctionality' de donde deriva A1..E1, en un esquema de herencia único. 'SpecificBase' deriva de' Base', por supuesto. – Constantinius

+0

Se me permite cambiar A1 ... E1. Creo que estás describiendo la solución 2 propuesta por mí. – dimba

+0

no exactamente, ya que está utilizando la herencia múltiple (hm ... su sintaxis parece incorrecta.) – Constantinius

Respuesta

3
struct Base { 
    virtual void PerformFunctionality() { 
    stuff(); 
    SpecificPartOfFunctionality(); 
    more_stuff(); 
    } 
private: 
    virtual void SpecificPartOfFunctionality() {} 
}; 

template<class Prev> 
struct Version3 : Prev { 
protected: // Not private if v4's override might need to call this. 
    virtual void SpecificPartOfFunctionality() { 
    v3_specific_stuff(); 
    } 
}; 

struct A1 : Base {}; 
struct A2 : A1 {}; 
struct A3 : Version3<A2> {}; 

struct B1 : Base {}; 
struct B2 : B1 {}; 
struct B3 : Version3<B2> {}; 

El único inconveniente es que no puede remitir fácilmente constructores en C++ actual, y probablemente va a utilizar siempre el constructor por defecto para A2 y B2 sólo por simplicidad.

En C++ 0x, sin embargo, puede reenviar constructores:

template<class Prev> 
struct Version3 : Prev { 
    using Prev::Prev; 
    //... 
}; 

struct C2 : C1 { 
    C2(int); // No default constructor. 
}; 
struct C3 : Version3<C2> { 
    C3() : Version3<C2>(42) {} 
}; 

Tenga en cuenta que una debe heredar directamente de An-1, ya que una prevalece sobre métodos específicos de An-1 .

Ha malentendido algo. No es necesario heredar directamente desde An-1. Por ejemplo, esto funciona muy bien:

struct Example { 
    virtual void f(); 
}; 

template<class B> 
struct Intermediate : B {}; 

struct Concrete : Intermediate<Example> { 
    virtual void f(); // Overrides Example::f. 
}; 

Recuerde que si esto no lo hicieron trabajo, entonces su Una no podía anular SpecificPartOfFunctionality de su clase base en el primer lugar! An no hereda directamente de Base en ninguno de sus ejemplos.

+0

El hecho de que An deba derivarse directamente de An-1 no se relaciona con mi pregunta. An es un subtipo de An-1 y, como tal, An-1 define algunas funciones virtuales anuladas por An. – dimba

+1

Pero luego su código anterior aún funciona. El término directamente es el incorrecto. An debería derivarse de An-1 (no necesariamente directamente). –

+0

@ChristianRau, sí, tienes razón – dimba

1

Defina una función de amigo en el alcance global que implementa la funcionalidad, y tenga SpecificPartsOfFunctionality haciendo nada más que llamarlo. P.ej.

void FunctionalityN(Base* b) 
{ 
    b->DoThings(); 
    ... 
} 

class An : public Base 
{ 
    friend void FunctionalityN(); 
    virtual void SpecificPartOfFunctionality() { FunctionalityN(this); } 
    .... 
}; 

Estrictamente hablando FuncionalidadN ni siquiera tendría que ser un amigo, aunque haría las cosas más fáciles. Esto hace que extender a Fn, Gn, etc. directamente y posteriormente reemplazar FunctionalityN con NewFunctionalityN es muy fácil, también.

+0

+1 @Matt. Esta es la variación mejorada de la solución 1, mientras que tiene una implementación para SpecificPartOfFunctionality() dentro de An..En. – dimba

+0

@dimba Derecho. Esto no elimina por completo la duplicación de código, aunque sí la minimiza, por lo que podría ser un compromiso aceptable ya que existe una tensión inherente entre la generalidad (un método para todos) y la escalabilidad (puede ser anulada posteriormente), y el hecho de que no puede cambiar Base (supongo). Observo que si puedes declarar 'SpecificPartOfFunctionality' pure virtual en' Base' o en otro lugar, entonces evitarás el problema de que futuros desarrolladores se olviden de anularlo. –

+0

No, eso no evitaría el problema de futuros desarrolladores que se olvidan de anularlo. Porque 1) versiones posteriores, p. A9 donde primero reemplaza SpecificPart en A3, debe considerar si es necesario anularlo, sin error de compilación si no es así, pero 2) si es puro virtual, entonces A1 y A2, que no necesitan SpecificPart, pueden ' ¡Se instanciará! –

Cuestiones relacionadas