Skip to content

Latest commit

 

History

History
365 lines (269 loc) · 6.7 KB

File metadata and controls

365 lines (269 loc) · 6.7 KB

Programowanie obiektowe

Dziedziczenie

Coders School

Wprowadzenie do dziedziczenia

Podczas implementacji klas, często możemy zauważyć, że część cech składowych klasy można wykorzystać także w innych klasach.

Weźmy pod lupę klasę Computer. Jeżeli chcielibyśmy utworzyć klasy: Laptop, PC, Tablet, to część metod oraz składowych klasy musielibyśmy powielić.


class Computer

class Computer {
public:
    void turnOn();
    void powerOff();
    void restart();

private:
    Processor processor_;
    Drive drive_;
    Motherboard motherboard_;
    GraphicsCard graphics_card_;
    Memory memory_;
};

class Laptop

class Laptop {
public:
    void turnOn();
    void powerOff();
    void restart();
    void display();
    void getUserInput();

private:
    Processor processor_;
    Drive drive_;
    Motherboard motherboard_;
    GraphicsCard graphics_card_;
    Memory memory_;
    Screen screen_;
    Keyboard keyboard_;
};

class Tablet

class Tablet {
public:
    void turnOn();
    void powerOff();
    void restart();
    void display();
    void getUserInput();

private:
    Processor processor_;
    Drive drive_;
    Motherboard motherboard_;
    GraphicsCard graphics_card_;
    Memory memory_;
    Screen screen_;
};

Jak uprościć strukturę naszego programu?

class Computer {
public:
    void turnOn();
    void powerOff();
    void restart();

protected:
    Processor processor_;
    Drive drive_;
    Motherboard motherboard_;
    GraphicsCard graphics_card_;
    Memory memory_;
};


class Laptop : public Computer {
public:
    void display();
    void getUserInput();

private:
    Screen screen_;
    Keyboard keyboard_;
};


class Tablet : public Computer {
public:
    void display();
    void getUserInput();

private:
    Screen screen_;
};

Klasy bazowe i pochodne

Klasa, po której dziedziczymy, nazywają się klasą bazową (base class).

Klasa, która dziedziczy nazywa się klasą pochodną (derived class).

Inaczej, klasa, po której dziedziczymy to rodzic (parent class).

Klasa, która dziedziczy to dziecko (child class).


Co z metodami klas Laptop i Tablet?

Czy można wydzielić kolejną klasę?

void display();
void getUserInput();

Klasa Screen i TouchScreen

Załóżmy, że dodajemy klasę Screen. Klasa ta wyświetla na bieżąco interfejs użytkownika.

Chcemy też stworzyć klasę reprezentującą ekran dotykowy - TouchScreen, który również umożliwia odczyt akcji od użytkownika i ich wyświetlanie.

class Screen {
public:
    void display();

private:
    void process();

    Monitor monitor_;
};
class TouchScreen {
public:
    void display();
    void getUserInput();

private:
    void process();
    void displayKeyboard();

    Monitor monitor_;
};

Jak uprościć powyższy kod?


Wykorzystanie dziedziczenia do uproszczenia kodu

class Screen {
public:
    void display();

private:
    void process();

    Monitor monitor_;
};
class TouchScreen : public Screen {
public:
    void getUserInput();

private:
    void displayKeyboard();
};

Wielodziedziczenie

class Screen {
public:
    void display();

private:
    void process();

    Monitor monitor_;
};

class TouchScreen : public Screen {
public:
    void getUserInput();

private:
    void displayKeyboard();
};

class Computer {
public:
    void turnOn();
    void powerOff();
    void restart();

protected:
    Processor processor_;
    Drive drive_;
    Motherboard motherboard_;
    GraphicsCard graphics_card_;
    Memory memory_;
};

class Laptop : public Computer,
               public Screen {
    Keyboard keyboard_;
public:
    void getUserInput();
};

class Tablet : public Computer,
               public TouchScreen {
};

Wielodziedziczenie - disclaimer

Wielodziedziczenie to dziedziczenie z kliku klas bazowych.

Wybór implementacji zależy od programisty.

Nie zawsze wielodziedziczenie będzie lepszym rozwiązaniem.

Należy się zawsze zastanowić czy dziedziczenie po konkretnej klasie uprości nam program i czy nie będzie powodować żadnych komplikacji w dalszym procesie rozbudowy naszego programu.

Najwyżej trzeba będzie refaktoryzować program ;)


Dziedziczenie - problemy

struct Bird {
    fly();
    makeSound();
};

struct Penguin {
    swim();
    makeSound();
};

// Hummingbird is the type of bird which makes so little sound so it
// can be said that it makes no sound.
struct Hummingbird {
    fly();
};

Dziedziczenie - zasada LSP

Jeżeli spróbujemy teraz uprościć klasę poprzez dziedziczenie pojawi się problem:

struct Bird {
    fly();
    makeSound();
};

struct Penguin : public Bird {
    fly();  // But I can't fly!
    swim();
    makeSound();
};

struct Hummingbird : public Bird {
    fly();
    makeSound();  // But I don't make sound!
};

Jeszcze bardziej utrudnimy sytuację, gdy w przyszłości dodamy sobie kolejne klasy jak Struś. Zawsze przed implementacją musimy się zastanowić jak podzielić odpowiedzialność na poszczególne klasy, aby uniknąć podobnych problemów.


Dla ciekawskich

Poczytajcie o zasadzie Liskov Substitution Principle (LSP). Mówi ona jak powinno / nie powinno się projektować kodu obiektowego. Ta zasada została złamana w ostatnim przykładzie.

Możecie też poczytać o wszystkich zasadach SOLID.


Q&A