2012-09-26 20 views
5

Mam klasy A i B. Teraz muszę napisać nową klasę C, która będzie używać pól i metod zarówno w A, jak iw B, ale nie we wszystkich. (Użyję około 50% rzeczy z A i B).Dziedziczenie tylko do ponownego użycia kodu C++

W tej chwili myślę o dziedziczeniu z A i B. Ale to spowoduje, że C zawiera wiele pól i metod, które nie mają znaczenia.

Zasadniczo używam dziedziczenie wyłącznie w celu ponownego wykorzystania kodu, bo inaczej będę musiał kopiować i wklejać wiele linii kodu z A i B.

Jest to praktyka bardzo źle? Czy istnieje inne podejście?

Dzięki!

+0

Dzięki za odpowiedzi chłopaki! Naprawdę je doceniam. Powodem, dla którego rozważałem używanie dziedziczenia zamiast kompozycji, jest to, że muszę również zmodyfikować niektóre zachowania w A i B. Również ja naprawdę nie chcę edytować A i B. – user1657624

Odpowiedz

2

To jest złe. Używaj dziedziczenia tylko wtedy, gdy logicznie ma to sens.

użycie zamiast skład (private dziedziczenie jest formą kompozycji, ale lepiej iść starej szkoły):

class C 
{ 
    A a; 
    B b; 
} 

Jeśli C nie składa się z A i B, można również zachować wskaźniki zamiast, tak rozmiar C nie zwiększa się.

3

Dziedziczenie powinno korzystać z paradygmatu "jest".
To oznacza, że ​​C powinno odziedziczyć po A, B, jeśli jest to rodzaj A i B.
Jeśli już korzystasz z dziedziczenia wielokrotnego, możesz podzielić A i B na wiele klas, z których każda zajmuje się małym fragmentem kodu, a następnie dziedziczy C tylko z tego, czego potrzebujesz - w ten sposób C zajmie tylko przestrzeń, której potrzebuje (zakładając, że A i B mają wielu członków).
Należy pamiętać, że metody elementów, które dotyczą tylko członków, nie mają wpływu na wielkość klasy.

+1

W C++ * publiczny * dziedziczenie oznacza "jest-a" . Inne poziomy dziedziczenia oznaczają coś innego ... jak "jest zaimplementowany w terminach". – cHao

+0

@ cHao - to prawda, ale nawet z prywatnym dziedziczeniem - C mający dostęp do części kodu, których nie potrzebuje, to zła praktyka. Poza tym - jeśli A i B mają wiele elementów danych, rozmiar C będzie nadal większy niż powinien. –

6

Dziedziczenie nie ma pojęcia "jest połową", więc zdecydowanie nie jest to sposób, aby tu wejść. Brzmi to jak główna sprawa dla kompozycji.

Wygląda na to, że A i B mają więcej niż jedną "funkcję", ponieważ połowa z nich wystarcza do skomponowania C.

nie jestem pewien, że ten ma zastosowanie w przypadku, ale za łamanie na dwie części, A1 i A2 z wymaganą funkcjonalność dla C w A1. Następnie wykonaj to samo dla B w B1 i B2.

A będzie wtedy kompozycja A1 i A2, B będzie kompozycja B1 i B2 i C będzie kompozycja A1 i B1. Żadne z nich nie ma żadnej niezbędnej funkcjonalności ani danych.

3

Jak Herb Sutter i Andrei Alexandrescu wyjaśnić w swojej książce C++ Coding Standards (item 34), dziedziczenie jest jednym z najbardziej napiętych stosunków C++, która pozwala na używanie dwóch klasach - To jest drugi tylko do friend relacji między klasami.

Zgodnie z S.O.L.I.D principles of OO udany projekt powinien mieć na celu luźne połączenie między klasami - to sugeruje utrzymywanie zależności do minimum; co z kolei oznacza, że ​​dziedziczenie jest narzędziem, które powinno być używane z ostrożnością.

Oczywiście, zależności generalnie muszą istnieć gdzieś, więc rozsądny kompromis może być dziedziczenie z klas bez wdrożenia na wszystkich (analogicznie do tzw interface s w językach takich jak C# i Java). na przykład

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. */ }; 

Z takim podejściem, jeśli masz elementy kodu które są ponownie wykorzystywane między kilkoma napędzane klas, byś zazwyczaj korzystają składu lub jakąś inną słabszą zależność wyeliminować powtarzające się kod.
np. Może chcesz ponownie użyć kodu do TurnLeft dla Bus i Car ale nie Train gdzie obrót w lewo jest nielogiczne, więc TurnLeft może skończyć się w osobnej klasy, który jest członkiem-of Bus i Car.

  • Ponadto każda funkcja, która może wiedzieć o wszystkich niejasno związanych klas byłoby na zewnątrz heirarchy klasy, tylko świadomość interfejsu/base zamiast szczegółów implementacji sedna.

Końcowym rezultatem może być niewielka ilość dodatkowego kodu do kompozycji, ale często mniej złożonego projektu, zazwyczaj łatwiejszego do zarządzania. Nie ma żadnych sztywnych zasad projektowania takiego kodu, ponieważ zależy on wyłącznie od unikalnych problemów, które próbujesz rozwiązać.

Istnieją inne sposoby ponownego użycia kodu bez napięty sprzężenia też - szablony pozwalają niejawnie definiowania interfejsów bez pustych klas zawierające czysty virtual funkcje (szablony zapewnienia dodatkowego bezpieczeństwa typ - co jest bardzo dobry sprawa, ale są składniowo trochę bardziej złożony);

Są też sposoby, dzięki którym można użyć kodu std::function i lambdas w celu ponownego użycia kodu w bardziej funkcjonalnym stylu - ponownie zwykle nie występują ścisłe zależności podczas przechodzenia wokół obiektów funkcyjnych.

0

Brzmi jak można korzystać z refaktoryzacji kodu:

Zamiast tego:

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! 
}; 

rozważyć dzielenie się, że koncepcyjnie różne części A i B, które mają w 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! 
}; 
Powiązane problemy