23.2 — Skład

Kompozycja obiektu

W rzeczywistym życiu fizycznym są często wbudowane z mniejszymi, prostszymi obiektami. Na przykładzie samochodu jest z metalowej ramy, silnika, niektórych opon, skrzyni biegów, i wielu innych części. Komputer osobisty składa się z płyty głównej, pewnej ilości pamięci itp. Jesteś ty ciała z mniejszą częścią: masz ciało, trochę nóg, ramiona i dalej. Dziesięć procesów złożonych z prostszych się nazywa komowanie obiektu.

Określenie najogólniejszej konfiguracji obiektu modeluje „ma-a” pomiędzy obiektami. Samochód „ma” skrzynię biegów. Twój komputer „ma” procesor. Ty „masz” serce. Obiekt złożony jest całością lub rodzicem. Prostszy obiekt jest często określany jako element, element lub komponentem.

W C++ widzieliśmy już, że struktura i klasa mogą mieć elementy danych różnych typów (takie jak typy podstawowe lub inne klasy). Kiedy tworzymy klasy ze składami danych, konstrukcję konstruujemy uniwersalny obiekt z prostszych części, co jest wyposażeniem obiektu. Z tego powodu struktura i klasy są czasami określane jako typ złożony.

Obiekt Composition jest rozwiązaniem w odniesieniu do C++, ponieważ pozwala na utworzenie systemu klasy poprzez podłączenie prostszych, łatwiejszych w zarządzaniu częściami. Zmniejsza to złożoność i pozwala na zapisanie kodu bezpieczeństwa i innych błędów, dzięki zastosowaniu ponownego kodu, który został już napisany, zatwierdzony i zweryfikowany jako krytyczny.

Typ aplikacyjny

Pierwsze dwa podstawowe podtypy urządzenia: kompozycja i agregacja. W tej konsekwencji wystąpimy, a w następstwie agregacji.

Uwaga terminologiczna: termin „kompozycja” jest często używany, ale dotyczy tylko definicji, jak i agregacji, a nie tylko do podtypu założycielskiego. W tym samouczku będzie obowiązywać termin „kompozycja obiektu”, gdy będzie obowiązywać do obu, oraz „kompozycja”, gdy będzie obowiązywać ograniczenia do podtypu graficznego.

Kompozycja

Aby zakwalifikować się jako kompozycję, obiekt i część pozostają w następującym powiązaniu:

  • Część (element) jest częścią obiektu (klasa)
  • Część (element) może należeć tylko do jednego obiektu (klasy) w określonym momencie
  • Część (element) ma swoje istnienie zarządzane przez obiekt (klasę)
  • Część (element) nie wie o pozostałości obiektu (klasy)

Dobrym ujawnieniem z życia wziętego jest połączenie pomiędzy ciałem człowieka a sercem. Przyjrzyjmy się temu bardziej szczegółowo.

Relacje dotyczące relacji części-całości, w których część muzyczna stanowi część całości. Na przykład serce jest częścią ciała człowieka. Udostępnienie może być tylko jednym obiektem na raz. Serce zawarte w ciałach jednej osoby nie może być częścią ciała innej osoby.

W powiązaniu z kompozycyjnym obiektem jest odpowiedzialny za istnienie części. Oznacza to, że część jest utworzona podczas tworzenia obiektu i niszczenia, gdy obiekt ulega zniszczeniu. Ale szerzej oznacza to, że obiekt zarządzania cyklem życia części w taki sposób, użytkownik obiektu nie musi się w to angażować. Na przykład, kiedy jest ciało, które jest również sercem. Kiedy ciało człowieka uległo zniszczeniu, jego serce uległo zniszczeniu. Z tego powodu kompozycja nazywa się czasem „relacją śmierci”.

I w końcu część nie wie o całości całości. Twoje serce działa w błogiej nieświadomości, że jest częścią większej struktury. Nazywamy to zależnością jednokierunkową , ponieważ ciało wie o sercu, ale nie na odwrót.

Zauważ, że skład nie ma nic do zastosowania na temat możliwości przenoszenia części. Serce można przenieść z jednego ciała do drugiego. Jednakże po przekazieniu nadal spełniają wymagania dotyczące oznaczenia (serce jest teraz charakterystyczny dla odbiorców i może być częścią obiektu, chyba że zostanie ponownie przeniesiony).

Nasza wszechobecna klasa Fraction jest dodatkowym oznaczeniem:

class Fraction
{
private:
	int m_numerator;
	int m_denominator;
 
public:
	Fraction(int numerator=0, int denominator=1)
		: m_numerator{ numerator }, m_denominator{ denominator }
	{
	}
};

Ta klasa ma dwa elementy danych: licznik i mianownik. Licznik i mianownik są częścią ułamka (zawartego w nim). Nie można należeć do więcej niż jednej frakcji na raz. Licznik i mianownik nie dotyczy, że są częścią ułamka, przechowywaną po prostu w liczbie pojedynczej. Kiedy utworzona jest instancja Fraction, utworzony jest licznik i mianownik. Kiedy instancja ułamka uległa zniszczeniu, licznik i mianownik został również zniszczeniu.

Podczas, gdy modele przedstawiają relacje typu (ciało ma serce, ułamek ma mianownik), można być bardziej szczegółowym i powiedzieć, że kompozycja modeluje relacje „część” (serce jest częścią ciała, licznik jest ułamka). Kompozycja jest często używana do modelowania połączeń fizycznych, w których jeden obiekt jest następstwem w innym.

Części zagrożenia mogą być dostępne lub multiplikatywne — na przykład serce jest pojedynczą strukturą, ale ciało zawiera 10 cyfrowych (które można modelować jako tablicę).

Wdrażanie powiadomienia

Kom przekaz do jednego z głównych komunikatów do zaimplementowania w C++. Zawierają elementy jako strukturę lub klasy ze zwykłymi elementami danych. Ponieważ te elementy danych wyodrębniają się bezpośrednio jako część struktury/klasy, ich czasy życia są powiązane z okresem istnienia samej klasy.

Kompozycja, która wymaga określonej alokacji lub dezalokacji, może być zaimplementowana przy użyciu wskaźników elementów danych. W przypadku wystąpienia konieczności bycia odpowiedzialnym za niezbędne zarządzanie pamięcią sama (a nie użytkownika klasy).

Ogólnie rzecz biorąc, jeśli możesz zaprojektować klasę przy użyciu aplikacji, powinieneś zaprojektować klasę przy rejestracji. Rozwiązanie przy użyciu rozwiązania to proste, elastyczne i solidne (w tym sensie, że ładnie po sobie sprzątają).

Więcej rozwiązań

W wielu grach i podstawowych narzędziach lub narzędziach poruszających się po planszy, mapie lub ekranie. Cechą wszechstronną wszystkich tych stworzeń/obiektów jest to, że wszystkie mają swoją lokalizację. W tym miejscu utworzymy klasę tworzenia, która będzie dostępna w klasach punktów przechowywania danych.

Najpierw zaprojektujmy klasę punktów. Nasze utworzenie będzie żyło w świecie 2d, więc nasza klasa punktów będzie miała 2 wymiary, X i Y. Założymy, że świat składa się z dyskretnych kwadratów, więc te wymiary będą zawsze liczbami całkowitymi.

Point2D.h:

#ifndef POINT2D_H
#define POINT2D_H

#include <iostream>

class Point2D
{
private:
    int m_x;
    int m_y;

public:
    // A default constructor
    Point2D()
        : m_x{ 0 }, m_y{ 0 }
    {
    }

    // A specific constructor
    Point2D(int x, int y)
        : m_x{ x }, m_y{ y }
    {
    }

    // An overloaded output operator
    friend std::ostream& operator<<(std::ostream& out, const Point2D& point)
    {
        out << '(' << point.m_x << ", " << point.m_y << ')';
        return out;
    }

    // Access functions
    void setPoint(int x, int y)
    {
        m_x = x;
        m_y = y;
    }

};

#endif

Zauważ, że wszystkie nasze funkcje zaimplementowane w pliku nagłówkowym (w celu zachowania zwięzłości przykładu), nie ma Point2D.cpp.

Ta klasa Point2d jest z systemem jej części: lokalizacja x i y są częścią Point2D, a ich czas życia jest powiązany z instancją Point2D.

Teraz zaprojektujmy nasze stworzenie. Nasz Stwór będzie miał kilka właściwości: który będzie ciągiem znaków oraz lokalizacją, która będzie naszą klasą Point2D.

Creature.h:

#ifndef CREATURE_H
#define CREATURE_H

#include <iostream>
#include <string>
#include <string_view>
#include "Point2D.h"

class Creature
{
private:
    std::string m_name;
    Point2D m_location;

public:
    Creature(std::string_view name, const Point2D& location)
        : m_name{ name }, m_location{ location }
    {
    }

    friend std::ostream& operator<<(std::ostream& out, const Creature& creature)
    {
        out << creature.m_name << " is at " << creature.m_location;
        return out;
    }

    void moveTo(int x, int y)
    {
        m_location.setPoint(x, y);
    }
};
#endif

To Stwór jest również złożeniem ich części. Imię i lokalizacja tworzenia jednego rodzica, a ich czas życia jest powiązany z istniejącym życiem Istoty, którego jest częścią.

I na końcu main.cpp:

#include <string>
#include <iostream>
#include "Creature.h"
#include "Point2D.h"

int main()
{
    std::cout << "Enter a name for your creature: ";
    std::string name;
    std::cin >> name;
    Creature creature{ name, { 4, 7 } };
	
    while (true)
    {
        // print the creature's name and location
        std::cout << creature << '\n';

        std::cout << "Enter new X location for creature (-1 to quit): ";
        int x{ 0 };
        std::cin >> x;
        if (x == -1)
            break;

        std::cout << "Enter new Y location for creature (-1 to quit): ";
        int y{ 0 };
        std::cin >> y;
        if (y == -1)
            break;
		
        creature.moveTo(x, y);
    }

    return 0;
}

Oto transkrypcja uruchamiania kodu:

Enter a name for your creature: Marvin
Marvin is at (4, 7)
Enter new X location for creature (-1 to quit): 6
Enter new Y location for creature (-1 to quit): 12
Marvin is at (6, 12)
Enter new X location for creature (-1 to quit): 3
Enter new Y location for creature (-1 to quit): 2
Marvin is at (3, 2)
Enter new X location for creature (-1 to quit): -1

Warianty motywu przesłanego

Chociaż uruchomiony jest bezpośrednio przez twoją część powiadomienia i bezpośrednio je, gdy wiadomość o wysłaniu wiadomości, wiadomość wysłana, wiadomość naginają te zasady.

Na przykład:

  • Kompozycja może być odłożona na inne części do czasu, aż będą potrzebne. Na przykład klasa stringów może nie mieć zastosowania do znaków, użytkownik nie przydzieli łańcuchowi danych do przechowywania.
  • Kompozycja może się pojawić na części, która została przekazana jako dane wejściowe, być sformułowana samodzielnie.
  • Kompozycja może delegować niszczenie swoich części w innym obiekcie (np. proceduraze zbierania śmieci).

Kluczowe otwarcie jest tutaj, że częścią zestawu są części bez użycia użytkownika zestawu zarządzania czymkolwiek.

Kompozycja i elementy klasy

Jedno z pytań, które teraz programiści często zadają, jeśli działają o kompozycję obiektów, brzmi: „Kiedy powinniem używać klas zamiast bezpośredniej implementacji funkcji?”. Na przykład zamiast alternatywnej klasy Point2D do implementacji lokalizacji Creature, moglibyśmy po prostu pobrać 2 liczby kontrolne do klasy Creature i zapisać kod w klasie Creature do obsługi pozycjonowania. efekt działania Point2D własnej klasą (i składnik Creature) ma wiele korzyści:

  1. Każda indywidualna klasa może być tylko prosta i bezpośrednioa, skupiona na dobrym wykonaniu jednego zadania. Dzięki temu te klasy są łatwiejsze do napisania i większego wsparcia, ponieważ są bardziej skoncentrowane. Na przykład Point2D odnosi się tylko do kwestii liczbowych z punktami, co pomaga w różnicowaniu prostotę.
  2. Każda klasa może być samodzielna, co stanowi je wyposażenie użytkowe. Na przykład moglibyśmy ponownie udostępnić naszą klasę Point2D w zupełnie innej aplikacji. Lub jeśli istnieją elementy powiązane z innym punktem (na przykład miejsca docelowego, do którego jest dostępne), można po prostu podłączyć jednostkę składową Point2D.
  3. W klasie elementów klasycznych, które mogą być pierwotnie stosowane, a które jest zastąpione tym, że jest koordynowane przez dane między urządzeniami. Pomaga w uruchomieniu złożoności klasycznej, ponieważ może delegować zadania swoim członkom, którzy już posiadają uprawnienia, jak te zadania wykonywane. Na przykład, kiedy przenosimy nasze stworzenie, delegujemy je na zadanie do klasy Point, które już wie, jak ustawić punkt. Tylko klasa Creature nie musi się uczyć, jak takie rzeczy zostały zaimplementowane.

Wskazówka

Dobrą praktyczną zasadą jest to, że każda klasa może być dodana tak, aby wykonać jedno zadanie. Zadaniem tym powinno być albo dostarczanie i umieszczanie określonych danych (np. Point2D, std::string), LUB koordynacja ich elementów (np. Creature). Idealnie nie jedno i drugie.

W tym przypadku ma sens, że Istota nie powinna się o sposób implementacji Punktów ani sposób przechowywania nazwy. Zadaniem stworzenia nie jest poznanie tych intymnych narzędzi. Zadaniem istoty jest martwienie się o to, jak koordynować przepływ danych i upewnić się, że każdy z członków klasy wie co ma mieć. To poszczególne klasy mają się uczyć jak jak to zrobią.

guest
Twój adres e-mail nie wychodzi składniki
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze powodujące z poprawkami usuniętymi po przetworzeniu, aby rozwiązać problem. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
201 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze