Dziś bardzo krótko, lecz treściwie!

Dziedziczenie w programowaniu obiektowym, to jeden z podstawowych mechanizmów. Kilka osób prosiło o tego typu wytłumaczenie podstawowych zagadnień, bo dawno na blogu takich aspektów programowania nie poruszałem.

Temat jest o tyle ciekawy, że w różnych językach może działać w trochę inny sposób. Przybliżę go na podstawie trzech języków, czyli C#, C++ oraz Python.

Pytania, które na wstępie się pojawiają to:

  • Czym jest dziedziczenie?
  • Jakie mamy rodzaje dziedziczenia?
  • Jak wygląda implementacja w językach Python, C# czy C++?

Czas wygryźć się w pytania. Zaczynajmy!

Dziedziczenie w programowaniu – Wprowadzenie

W dzisiejszych czasach dziedziczenie w programowaniu jest kluczowe i nie istniałyby współczesne języki i programowanie obiektowe. To jest fakt i ciężko programiście się z nim nie zgodzić. Czy da się odpowiedzieć jednym zdaniem, czym właściwie jest dziedziczenie w programowaniu?

Pokuszę się o to i powiem, że dziedziczenie to

Specjalny mechanizm, który pozwala na współdzielenie funkcjonalności pomiędzy klasami.

Pachnie zdaniem wyrwanym z Wikipedii. Jest jednak prawdziwe i dla wielu programistów wystarczające, bo w zasadzie opisuje pobieżnie całą ideę dziedziczenia.

Przejdźmy do bardziej szczegółowego analizowania.

Istota dziedziczenia

Nie da się ukryć, że cecha języka, jaką jest klasa, nie byłaby tak pomocna i potrzebna gdyby nie dziedziczenie w programowaniu. Temat klas i ogólnie obiektów pominę, bo to temat na obszerny artykuł i zakładam, że czytelnik wie lub doczyta w tym temacie.

Możemy sobie wyobrazić sytuację, w której tworzymy nasz kod i nagle dochodzimy do momentu, gdy musimy użyć metody, która była stworzona pewien czas temu w innym miejscu systemu. 

Chronologicznie mogłoby to wyglądać tak:

Utworzenie klasy A -> Utworzenie metody AA w klasie A -> Utworzenie klasy B -> Potrzeba skorzystania z metody AA

Jest to zjawisko dość częste i rozwiązań na tego typu sytuacje jest mnoga ilość. Jednym z nich jest dziedziczenie. Wyżej wspomniałem, że jest to użycie funkcjonalności pomiędzy klasami. I skoro w jednej i drugiej klasie potrzebujemy użyć metody AA, to warto użyć dziedziczenia. 

Można to zrobić w następujący sposób. Wprowadzamy klasę C, w której implementujemy metodę AA. Następnie w klasie A i klasie B tworzymy dziedziczenie po klasie C i już możemy cieszyć się metodą AA, która napisana została raz a użyta w wielu miejscach systemu.

Prosta implementacja w języku Python i C# może wyglądać następująco.

Python 3

# Python 3

class C:
    def AA(self):
        print('To jest metoda AA z klasy C')


class A(C):
    def FooA(self):
        print("To jest metoda FooA z klasy A")


class B(C):
    def FooB(self):
        print("To jest metoda FooB z klasy B")


if __name__ in '__main__':
    class_a = A()
    class_b = B()

    class_a.FooA()  # To jest metoda FooA z klasy A
    class_a.AA()  # To jest metoda AA z klasy C

    class_b.FooB()  # To jest metoda FooB z klasy B
    class_b.AA()  # To jest metoda AA z klasy C

C#

using System;

namespace Dziedziczenie
{
    public class C
    {
        public string AA()
        {
            return "To jest metoda AA z klasy C";
        }
    }

    public class B : C
    {
        public string FooB()
        {
            return "To jest metoda FooB z klasy B";
        }
    }

    public class A : C
    {
        public string FooA()
        {
            return "To jest metoda FooA z klasy A";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var class_a = new A();
            var class_b = new B();

            Console.WriteLine(class_a.FooA());     // To jest medoda FooA z klasy A
            Console.WriteLine(class_a.AA());       // To jest metoda AA z klasy C

            Console.WriteLine(class_b.FooB());     // To jest medoda FooB z klasy B
            Console.WriteLine(class_b.AA());       // To jest metoda AA z klasy C

            Console.ReadKey();
        }
    }
}

Jak widać, w przypadku języka Python czy C# dziedziczenie jest dość intuicyjne. Trochę inaczej ma się w przypadku pewnej cechy dziedziczenia, które dwa wspomniane języki nie praktykują. Przyjrzyjmy się więc rodzajom dziedziczenia!

Rodzaje dziedziczenia

Dziedziczenie w programowaniu możemy podzielić na trzy główne rodzaje. Są to:

  • Dziedziczenie pojedyncze – jest tak wtedy, gdy klasa pochodna dziedziczy tylko z jednej klasy bazowej. Warto mieć na uwadze fakt, że klasa bazowa może być użyta w innych klasach. Ten przypadek został zaprezentowany dokładnie w przykładach z językiem Python i C# powyżej.
  • Dziedziczenie wielopoziomowe – jak sama nazwa wskazuje, jest to forma dziedziczenia, gdzie klasa pochodna, jest dla innej klasy klasą bazową. Wyobraź sobie naszą klasę C, która jest klasą bazową dla klasy pochodnej A i B. Jednak z drugiej strony jest klasą pochodną w stosunku do nowo powstałej klasy bazowej D. Mamy więc hierarchię klasa D => klasa C => klasa B i A
# Python 3

class D:
    def AAPlus(self):
        print('To jest metoda AAPlus z klasy D')


class C(D):
    def AA(self):
        print('To jest metoda AA z klasy C')


class A(C):
    def FooA(self):
        print("To jest metoda FooA z klasy A")


class B(C):
    def FooB(self):
        print("To jest metoda FooB z klasy B")


if __name__ in '__main__':
    class_a = A()
    class_b = B()

    class_a.FooA()  # To jest metoda FooA z klasy A
    class_a.AA()  # To jest metoda AA z klasy C
    class_a.AAPlus()  # To jest metoda AAPlus z klasy D

    class_b.FooB()  # To jest metoda FooB z klasy B
    class_b.AA()  # To jest metoda AA z klasy C
    class_b.AAPlus()  # To jest metoda AAPlus z klasy D

  • Dziedziczenie wielokrotne – jest tak wtedy, gdy klasa pochodna może dziedziczyć więcej niż z jednej klasy bazowej. Językami, które pozwalają na taki rodzaj dziedziczenia, są, chociażby C++ czy Perl. Dziedziczenie wielokrotne jest jednak problematyczne i krytykowane przez większość programistów. Dzieje się tak, ponieważ po pierwsze, sprawia to problemy w implementacji, po drugie nie jest jednoznaczne to, w jakiej kolejności wykonywane są konstruktory klas i po trzecie powstaje problem niejednoznaczności semantycznej. 

Przykład takiego dziedziczenia prezentuję poniżej. Do znanego nam już przypadku z trzema klasami dodam klasę D, która będzie podobna dla klasy C, i po której dodatkowo będą dziedziczyć klasy A i B

C++

#include <iostream>

class D
{
    public:
        D()
        { }

        void BB()
        {
            std::cout << "To jest metoda BB z klasy D\n";
        }
};


class C
{
    public:
        C()
        { }

        void AA()
        {
            std::cout << "To jest metoda AA z klasy C\n";
        }
};

class B : public C, public D
{
    public:
        B()
        { }

        void FooB()
        {
            std::cout << "To jest metoda FooB z klasy B\n";
        }
 };

class A : public C, public D
{
    public:
        A()
        { }

        void FooA()
        {
            std::cout << "To jest metoda FooA z klasy A\n";
        }
};


int main()
{
    A class_a;
    B class_b;

    class_a.FooA();   // To jest metoda FooA z klasy A
    class_a.AA();     // To jest metoda AA z klasy C
    class_a.BB();     // To jest metoda BB z klasy D

    class_b.FooB();   // To jest metoda FooB z klasy B
    class_b.AA();     // To jest metoda AA z klasy C
    class_b.BB();     // To jest metoda BB z klasy D
}

Kod przy takim dziedziczeniu już zaczyna robić się kłopotliwa, a to tylko kilka małych klas. Pomyśl co dziać się będzie gdyby tych klas było setki albo tysiące i nagle przyjdzie nam szukać niejasnego błędu, a klasy ze sobą są przemieszane jak makaron w jednym z popularnych włoskich dań!

Dziedziczenie w programowaniu – Podsumowanie

To tyle, jeżeli chodzi o podstawy z tematu dziedziczenie w programowaniu. Niewątpliwie dziedziczenie mocno powiązane jest z polimorfizmem i hermetyzacją, które postaram się niebawem omówić w podobnej formie. Może powstanie z tego fajny mini cykl dotyczący programowania obiektowego? 

Daj znać, czy takie krótkie zajawki programistyczne są dla Ciebie ciekawe! 

Warto zapamiętać jeszcze jedną rzecz. Dziedziczenie w programowaniu obiektowym dość mocno powiązane jest z zasadami SOLID, o których pisałem we wpisie numer 5.

 

Na koniec podrzucę Ci jeszcze książki na temat programowania, które pozwolą Ci na samodzielną naukę.

PS. Będę wdzięczny za komentarze pod tym postem!

PS2. Czeka też na Ciebie prezent. Wystarczy, że zapiszesz się na Newsletter poniżej!

PS3. Dołącz do naszej grupy na Facebook!


Subskrybuj

Zapisz się na Newsletter, odbierz NAGRODĘ w postaci e-booka z 10 omówionymi algorytmami pojawiającymi się w pytaniach podczas REKRUTACJI..

Dodatkowo otrzymuj co niedzielę informacje na temat nowych wpisów, wiadomości ze świata IT i matematyki oraz ciekawych wydarzeniach. Nie przegap i dołącz już dziś do 920 osób!.

Źródła

https://docs.microsoft.com/pl-pl/dotnet/csharp/tutorials/inheritance
https://docs.microsoft.com/en-us/cpp/cpp/inheritance-cpp?view=vs-2019
https://docs.python.org/3/tutorial/classes.html
Autor

👨‍💻 .NET and Python programming passionate 🏦 Digtial Banking Solutions 🎓 Student 📊 Psychology 📚 Bookworm 🏠 Warsaw

1 Komentarz

  1. Jak dla mnie bomba! Takie zajawki mogą się pojawiać na blogu 🙂

    Mam pytanie co do dziedziczenia wielokrotnego, co jeżeli w klasie C i D znajdą się dokładnie te same metody (nazwa, przyjmowane parametry, zwracany typ)? Program się wykona ale nie będziemy mieli nad tym kontroli? Czy kompilator zwróci nam błąd?

    Pozdrawiam! 🙂

Napisz komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.