14.1 — Wprowadzenie do programowania obiektowego

Programowanie proceduralne

Wróciwszy do lekcji 1.3 -- Wprowadzenie do obiektów i zmiennych, zdefiniowaliśmy obiekt w C++ jako: „fragment pamięci, który może być używany do przechowywania wartości”. Obiekt posiadający nazwę nazywany jest zmienną. Nasze programy w języku C++ składają się z kolejnych list instrukcji wydawanych komputerowi, które definiują dane (poprzez obiekty) i operacje wykonywane na tych danych (poprzez funkcje zawierające instrukcje i wyrażenia).

Do tej pory zajmowaliśmy się rodzajem programowania zwanym programowaniem proceduralnym. W programowaniu proceduralnym nacisk położony jest na tworzenie „procedur” (które w C++ nazywane są funkcjami), które implementują logikę naszego programu. Do tych funkcji przekazujemy obiekty danych, funkcje te wykonują operacje na danych, a następnie potencjalnie zwracają wynik do wykorzystania przez osobę wywołującą.

W programowaniu proceduralnym funkcje i dane, na których te funkcje działają, są oddzielnymi bytami. Programista jest odpowiedzialny za połączenie funkcji i danych w celu uzyskania pożądanego rezultatu. Prowadzi to do kodu wyglądającego tak:

eat(you, apple);

Teraz rozejrzyj się wokół siebie — wszędzie, gdzie spojrzysz, są obiekty: książki, budynki, jedzenie, a nawet ty. Takie obiekty mają dwa główne elementy: 1) pewną liczbę powiązanych właściwości (np. wagę, kolor, rozmiar, solidność, kształt itp.) oraz 2) pewną liczbę zachowań, które mogą wykazywać (np. otwieranie, nagrzewanie czegoś innego itp.). Te właściwości i zachowania są nierozłączne.

W programowaniu właściwości są reprezentowane przez obiekty, a zachowania są reprezentowane przez funkcje. Zatem programowanie proceduralne dość słabo reprezentuje rzeczywistość, ponieważ oddziela właściwości (obiekty) od zachowań (funkcje).

Co to jest programowanie obiektowe?

W programowanie obiektowe (często w skrócie OOP), nacisk kładziony jest na tworzenie zdefiniowanych przez program typów danych, które zawierają zarówno właściwości, jak i zestaw dobrze zdefiniowanych zachowań. Termin „obiekt” w OOP odnosi się do obiektów, które możemy utworzyć z takich typów.

Prowadzi to do kodu, który wygląda mniej więcej tak:

you.eat(apple);

Dzięki temu staje się jaśniejsze, kim jest podmiot (you), jakie zachowanie jest wywoływane (eat()) i jakie obiekty są dodatkami do tego zachowania (apple).

Ponieważ właściwości i zachowania nie są są dłużej oddzielne, obiekty są łatwiejsze do modularyzacji, co ułatwia pisanie i zrozumienie naszych programów, a także zapewnia wyższy stopień możliwości ponownego wykorzystania kodu. Obiekty te zapewniają również bardziej intuicyjny sposób pracy z naszymi danymi, pozwalając nam zdefiniować sposób interakcji z obiektami i sposób, w jaki wchodzą one w interakcję z innymi obiektami.

Omówimy sposób tworzenia takich obiektów w następnej lekcji.

Przykład proceduralny a OOP

Oto krótki program napisany w stylu programowania proceduralnego, który wypisuje nazwę i liczbę nóg zwierzęcia:

#include <iostream>
#include <string_view>

enum AnimalType
{
    cat,
    dog,
    chicken,
};

constexpr std::string_view animalName(AnimalType type)
{
    switch (type)
    {
    case cat: return "cat";
    case dog: return "dog";
    case chicken: return "chicken";
    default:  return "";
    }
}

constexpr int numLegs(AnimalType type)
{
    switch (type)
    {
    case cat: return 4;
    case dog: return 4;
    case chicken: return 2;
    default:  return 0;
    }
}


int main()
{
    constexpr AnimalType animal{ cat };
    std::cout << "A " << animalName(animal) << " has " << numLegs(animal) << " legs\n";

    return 0;
}

W tym programie napisaliśmy funkcje, które pozwalają nam na takie rzeczy, jak uzyskanie liczby nóg zwierzęcia i uzyskanie nazwy zwierzęcia.

Chociaż działa to dobrze, zastanów się, co się stanie, gdy będziemy chcieli zaktualizować ten program tak, aby nasze zwierzę było teraz snake. Aby dodać węża do naszego kodu, musielibyśmy zmodyfikować AnimalType, numLegs(), animalName(). Gdyby to była większa baza kodu, musielibyśmy także zaktualizować każdą inną funkcję korzystającą z AnimalType -- jeśli AnimalType była używana w wielu miejscach, może to oznaczać dużo kodu, który wymaga zmiany (i potencjalnie jest uszkodzony).

Teraz napiszmy ten sam program (wytwarzający te same dane wyjściowe) przy użyciu większego OOP sposób myślenia:

#include <iostream>
#include <string_view>

struct Cat
{
    std::string_view name{ "cat" };
    int numLegs{ 4 };
};

struct Dog
{
    std::string_view name{ "dog" };
    int numLegs{ 4 };
};

struct Chicken
{
    std::string_view name{ "chicken" };
    int numLegs{ 2 };
};

int main()
{
    constexpr Cat animal;
    std::cout << "a " << animal.name << " has " << animal.numLegs << " legs\n";

    return 0;
}

W tym przykładzie każde zwierzę ma swój własny typ zdefiniowany w programie i ten typ zarządza wszystkim, co jest z nim powiązane (co w tym przypadku polega po prostu na śledzeniu nazwy i liczby nóg).

Rozważmy teraz przypadek, w którym chcemy zaktualizować nasze zwierzę do węża. Wszystko, co musimy zrobić, to utworzyć Snake typ i użyć go zamiast Cat. Należy zmienić bardzo niewiele istniejącego kodu, co oznacza znacznie mniejsze ryzyko uszkodzenia czegoś, co już działa.

Jak przedstawiono, nasz Cat, Dog, I Chicken przykład powyżej zawiera wiele powtórzeń (ponieważ każdy definiuje dokładnie ten sam zestaw elementów). W takim przypadku preferowane może być utworzenie wspólnej Animal struktury i utworzenie instancji dla każdego zwierzęcia. Ale co jeśli chcemy dodać nowego członka do Chicken który nie ma zastosowania do innych zwierząt (np. wormsPerDay)? W przypadku wspólnej Animal struktury wszystkie zwierzęta otrzymają ten element. Dzięki naszemu modelowi OOP możemy ograniczyć tego członka do Chicken obiekty.

OOP przynosi inne korzyści

W szkole, kiedy przesyłasz swoje zadania programistyczne, twoja praca jest w zasadzie skończona. Twój profesor lub asystent nauczyciela uruchomi Twój kod, aby sprawdzić, czy daje poprawny wynik. Albo tak, albo nie, i jesteś odpowiednio oceniany. Twój kod zostanie w tym momencie prawdopodobnie odrzucony.

Z drugiej strony, gdy prześlesz swój kod do repozytorium używanego przez innych programistów lub do aplikacji używanej przez prawdziwych użytkowników, to zupełnie inna gra. Niektóre nowe wersje systemu operacyjnego lub oprogramowania spowodują uszkodzenie kodu. Użytkownicy znajdą jakiś błąd logiczny, który popełniłeś. Partner biznesowy będzie wymagał nowych możliwości. Inni programiści będą musieli rozszerzyć Twój kod bez jego łamania. Twój kod musi mieć możliwość ewolucji, być może znaczącej, przy minimalnych nakładach czasu, minimalnych bólach głowy i minimalnych awariach.

Najlepszym sposobem rozwiązania tych problemów jest utrzymanie kodu w możliwie największej modułowości (i nienadmiarowości). Aby w tym pomóc, OOP przedstawia także szereg innych przydatnych koncepcji: dziedziczenie, enkapsulacja, abstrakcja i polimorfizm.

Nota autora

Projektanci języków kierują się filozofią: nigdy nie używaj małego słowa tam, gdzie wystarczy duże.

Dlaczego skrót słowa jest tak długi?

Omówimy to wszystko w odpowiednim czasie i jak mogą pomóc dzięki czemu Twój kod będzie mniej zbędny i łatwiejszy do modyfikowania i rozszerzania. Gdy już dobrze zaznajomisz się z OOP i okaże się, że zadziała, prawdopodobnie już nigdy nie będziesz chciał wracać do czystego programowania proceduralnego.

To powiedziawszy, OOP nie zastępuje programowania proceduralnego — raczej zapewnia dodatkowe narzędzia w zestawie narzędzi programistycznych, umożliwiające zarządzanie złożonością w razie potrzeby.

Termin „obiekt”

Zauważ, że termin „obiekt” jest nieco przeciążony, co powoduje pewne trudności zamieszanie. W tradycyjnym programowaniu obiekt to fragment pamięci przechowujący wartości. I to wszystko. W programowaniu obiektowym „obiekt” oznacza, że ​​jest zarówno obiektem w tradycyjnym sensie programowania, jak i łączy w sobie zarówno właściwości, jak i zachowania. W tych samouczkach będziemy preferować tradycyjne znaczenie terminu obiekt i preferować termin „obiekt klasy”, gdy konkretnie odnosimy się do obiektów OOP.

Czas quizu

Pytanie nr 1

Zaktualizuj powyższy przykład proceduralny dotyczący zwierząt i utwórz instancję węża zamiast kota.

Pokaż rozwiązanie

Pytanie nr 2

Zaktualizuj powyższy przykład przypominający zwierzęcy obiekt OOP i utwórz instancję węża zamiast kota.

Pokaż rozwiązanie

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
160 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze