2012-09-26 23 views
5

Tengo clases A y B. Ahora tengo que escribir una nueva clase C que usará algunos campos y métodos en A y B pero no en todos. (Usaré aproximadamente el 50% de las cosas de A y B).Herencia solo para la reutilización de código C++

En este momento estoy pensando en heredar tanto de A como de B. Pero eso hará que C contenga muchos campos y métodos que no tienen ningún significado.

Básicamente estoy usando la herencia sólo con fines de reutilización de código, porque de lo contrario voy a tener que copiar y pegar muchas líneas de código de A y B.

¿Es esta práctica muy mal? ¿Hay un enfoque diferente?

Gracias!

+0

Gracias por las respuestas chicos! Realmente los aprecio La razón por la que consideré usar herencia en lugar de composición es porque también necesito modificar algunos comportamientos en A y B. Además, realmente no quiero editar A y B. – user1657624

Respuesta

2

Es malo. Solo use herencia cuando lógicamente tenga sentido hacerlo.

Uso composición lugar (private herencia es una forma de composición, pero es mejor ir de la vieja escuela):

class C 
{ 
    A a; 
    B b; 
} 

Si C no está compuesto de A y B, también puede tener lugar punteros, por lo el tamaño de C no aumenta.

3

La herencia debe usar el paradigma "es un".
Lo que significa que C debe heredar de A, B si es una clase de A y B.
Si ya está utilizando la herencia múltiple, es posible que desee dividir A y B en varias clases, cada una con un pequeño fragmento de su código, y luego hereda C solo de lo que necesita, de esta manera C solo ocupará el espacio que necesita (suponiendo que A y B tienen muchos miembros).
Recuerde que el tamaño de su clase no se ve afectado por los métodos de los miembros, solo los datos de los miembros.

+1

En C++, * public * inheritance significa "is-a" . Otros niveles de herencia significan algo diferente ... como "es-implementado-en-términos-de". – cHao

+0

@cHao - eso es cierto, pero incluso con herencia privada - C tener acceso a partes de código que no necesita es una mala práctica. Además, si A y B tienen muchos miembros de datos, el tamaño de C seguirá siendo mayor de lo que debería ser. –

6

La herencia no tiene un concepto de "es la mitad de", por lo que definitivamente no es la manera de hacerlo aquí. Parece que es un buen ejemplo para la composición.

Parece que A y B tienen más de una "función", ya que la mitad de ellas son suficientes para componer un C.

No estoy seguro de que esto es aplicable en su caso, pero considero rompiendo una en dos partes, A1 y A2 con la funcionalidad requerida para C en A1. Luego haga lo mismo para B en B1 y B2.

A será entonces una composición de A1 y A2, B habrá una composición de B1 y B2 y C será una composición de A1 y B1. Ninguno de ellos con ninguna funcionalidad o datos innecesarios.

3

como Herb Sutter y Andrei Alexandrescu explican en su libro C++ Coding Standards (item 34), la herencia es una de las relaciones más estrechas, que C++ permite utilizar entre dos clases - Es el segundo a una relación friend entre clases.

De acuerdo con S.O.L.I.D principles of OO, un diseño exitoso debe apuntar a acoplamiento flojo entre clases - esto implica mantener las dependencias al mínimo; lo que a su vez implica que la herencia es una herramienta que debe usarse con cuidado.

Por supuesto, las dependencias en general, tienen que existir en algún lugar , por lo que un compromiso razonable se puede heredar de clases sin aplicación en absoluto (análoga a los llamados interface s en lenguajes como C# y Java). p.ej.

class IDriveable 
{ 
public: 
    virtual void GoForward() = 0; 
    virtual void GoBackward() = 0; 
}; 

class Car : public IDriveable { /* etc. */ }; 
class Bus : public IDriveable { /* etc. */ }; 
class Train : public IDriveable { /* etc. */ }; 

Con este enfoque, si tiene elementos de código que se reutilizan entre varias clases Disfruta de la conducción, lo más habitual es utilizar composición o alguna otra relación más débil para eliminar código repetido.
p. quizás desea volver a utilizar el código de TurnLeft para un Bus y una Car pero no un Train donde girando a la izquierda es ilógico, por lo TurnLeft podría terminar en una clase separada que es un miembro de Bus y Car.

  • Además, cualquier funcionalidad que pueda necesita saber acerca de todas las clases relacionadas vagamente sería externa a la jerarquía de clases, sólo es consciente de la interfaz/base en lugar de los detalles de implementación meollo de la cuestión.

El resultado final puede ser una pequeña cantidad de código adicional para la composición, pero a menudo un diseño menos complejo, y normalmente uno que es más fácil de administrar. No existen reglas estrictas para diseñar código como este, ya que depende completamente de los problemas únicos que intenta resolver.

Hay otras formas de reutilizar código sin estrecho acoplamiento también - plantillas permiten definir implícitamente las interfaces sin clases vacías que contienen puros virtual funciones (plantillas proporcionan la seguridad de tipos adicionales - que es una muy buena cosa, pero son sintácticamente una poco más complejo);

Y hay formas en las que puede usar std::function y lambdas para volver a utilizar el código en un estilo más funcional: una vez más, normalmente no hay dependencias estrictas al pasar alrededor de los objetos de función.

0

suena como usted podría beneficiarse de refactorización su código:

lugar de esto:

class A 
{ 
    virtual void foo(); 
    virtual void bar(); 
}; 

class B 
{ 
    virtual void biz(); 
    virtual void baz(); 
}; 

class C : public A, public B 
{ 
    virtual void foo() { /* my stuff */ } 
    virtual void biz() { /* my other stuff */ + 

    // but I don't need bar or baz! 
}; 

considerar dividir los conceptualmente diferentes partes A y B que desee en C:

class core_A 
{ 
    virtual void foo(); 
}; 

class A : public core_A 
{ 
    virtual void bar(); 
}; 

class core_B 
{ 
    virtual void biz(); 
}; 

class B : public core_B 
{ 
    virtual void baz(); 
}; 

class C : public core_A, public core_B 
{ 
    virtual void foo() { /* my stuff */ } 
    virtual void biz() { /* my other stuff */ + 

    // Great, I don't have bar or baz! 
}; 
Cuestiones relacionadas