Wprowadzenie do wzorców projektowych
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 90 z 104.
Wzorce projektowe to sprawdzone, wielokrotnego użytku rozwiązania typowych problemów pojawiających się podczas projektowania oprogramowania. Nie są to fragmenty kodu, które można bezpośrednio skopiować — to szablony lub wzorce opisujące, jak organizować klasy i obiekty, aby elegancko rozwiązywać konkretne problemy.
Koncepcja została spopularyzowana przez „Gang of Four” (GoF) w ich książce z 1994 roku, w której skatalogowano 23 podstawowe wzorce. Wzorce te zazwyczaj grupuje się w trzy kategorie:
| Kategoria | Cel | Przykłady |
|---|---|---|
| Creational | Mechanizmy tworzenia obiektów | Singleton, Factory, Builder |
| Structural | Kompozycja klas i obiektów | Adapter, Decorator, Composite |
| Behavioral | Interakcje i odpowiedzialność obiektów | Observer, Strategy, Command |
Dlaczego warto uczyć się wzorców projektowych? Zapewniają wspólny język programistom — stwierdzenie „użyj tutaj wzorca Factory” natychmiast przekazuje złożoną ideę. Pomagają też uniknąć wymyślania rozwiązań na nowo oraz sprawiają, że kod jest bardziej elastyczny i łatwiejszy w utrzymaniu.
W C++ wzorce projektowe wykorzystują funkcje OOP, których już się nauczyłeś: dziedziczenie, polimorfizm, klasy abstrakcyjne i szablony. W kolejnych lekcjach zaimplementujesz kilka podstawowych wzorców i dowiesz się, kiedy każdy z nich jest odpowiednim narzędziem do danego zadania.
Wyzwanie
ŁatwyStwórzmy prosty klasyfikator wzorców, który pokaże, że rozumiesz trzy kategorie wzorców projektowych. Utworzysz system, który przyjmuje nazwę wzorca i określa, do której kategorii należy: Creational, Structural czy Behavioral, a także podaje krótki opis celu tej kategorii.
Rozmieścisz kod w trzech plikach:
PatternCategory.h: Zdefiniuj abstrakcyjną klasę bazową o nazwiePatternCategory, która reprezentuje kategorię wzorców projektowych. Powinna zawierać:- Czysto wirtualną metodę
getName(), która zwraca nazwę kategorii jako ciąg znaków - Czysto wirtualną metodę
getPurpose(), która zwraca opis tego, do czego służą wzorce z tej kategorii - Wirtualny destruktor
Następnie utwórz trzy klasy pochodne dziedziczące po
PatternCategory:CreationalCategory: zwraca nazwę"Creational"i cel"Object creation mechanisms"StructuralCategory: zwraca nazwę"Structural"i cel"Class and object composition"BehavioralCategory: zwraca nazwę"Behavioral"i cel"Object interaction and responsibility"
- Czysto wirtualną metodę
PatternClassifier.h: Utwórz klasęPatternClassifier, która potrafi określić, do której kategorii należy dany wzorzec.Klasa klasyfikatora powinna mieć metodę
classify, która przyjmuje nazwę wzorca (ciąg znaków) i zwraca wskaźnik do odpowiedniej klasyPatternCategory. Do zarządzania pamięcią użyj inteligentnych wskaźników (std::unique_ptr).Klasyfikator powinien rozpoznawać następujące wzorce:
- Creational:
Singleton,Factory,Builder - Structural:
Adapter,Decorator,Composite - Behavioral:
Observer,Strategy,Command
Jeśli nazwa wzorca nie zostanie rozpoznana, zwróć
nullptr.- Creational:
main.cpp: Odczytaj jedno wejście: nazwę wzorca projektowego.Utwórz obiekt
PatternClassifieri użyj go do sklasyfikowania wzorca wejściowego. Jeśli wzorzec zostanie rozpoznany, wypisz:[PatternName] is a [CategoryName] pattern Purpose: [CategoryPurpose]Jeśli wzorzec nie zostanie rozpoznany, wypisz:
Unknown pattern: [PatternName]
Na przykład dla wejścia Singleton:
Singleton is a Creational pattern
Purpose: Object creation mechanismsDla wejścia Observer:
Observer is a Behavioral pattern
Purpose: Object interaction and responsibilityDla wejścia Proxy:
Unknown pattern: ProxyTo wyzwanie utrwala wiedzę o trzech kategoriach wzorców projektowych, a jednocześnie pozwala ćwiczyć dziedziczenie, polimorfizm i inteligentne wskaźniki — pojęcia, które już opanowałeś. Struktura wieloplikowa odzwierciedla sposób, w jaki rzeczywiste projekty porządkują powiązane klasy.
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "PatternClassifier.h"
int main() {
// Wczytaj nazwę wzorca
std::string patternName;
std::cin >> patternName;
// TODO: Utwórz instancję PatternClassifier
// TODO: Użyj klasyfikatora do sklasyfikowania wzorca wejściowego
// TODO: Jeśli wzorzec został rozpoznany, wypisz:
// "[PatternName] is a [CategoryName] pattern"
// "Purpose: [CategoryPurpose]"
// TODO: Jeśli wzorzec nie został rozpoznany (nullptr), wypisz:
// "Unknown pattern: [PatternName]"
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników10Przegląd STL
Przegląd i filozofia STLKontenery STLIteratoryAlgorytmy STLFunktory i wyrażenia lambdaPowtórzenie – częstość występowania słów13Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonFabryka i fabryka abstrakcyjnaWzorzec BuilderWzorzec ObserverWzorzec Strategy2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online