Wzorzec stanu
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 60 z 70.
Wzorzec State to wzorzec behawioralny, który pozwala obiektowi zmieniać swoje zachowanie, gdy zmienia się jego stan wewnętrzny. Zamiast używać złożonych instrukcji warunkowych do obsługi różnych stanów, enkapsulujesz każdy stan we własnej klasie i delegujesz zachowanie do obiektu reprezentującego bieżący stan.
Wzorzec składa się z trzech komponentów: interfejsu stanu definiującego zachowanie specyficzne dla danego stanu, konkretnych klas stanów implementujących to zachowanie oraz klasy kontekstu przechowującej odwołanie do bieżącego stanu:
public interface IDocumentState
{
void Publish(Document doc);
}
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}Kontekst przechowuje bieżący stan i deleguje do niego działania. Obiekty stanów mogą wywoływać przejścia, wywołując SetState() na kontekście:
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
var doc = new Document();
doc.Publish(); // Przekazywanie do moderacji
doc.Publish(); // Publikowanie dokumentu
doc.Publish(); // Już opublikowanoWzorzec State eliminuje rozbudowane instrukcje if-else lub switch, które sprawdzają bieżący stan. Każda klasa stanu obsługuje wyłącznie własne zachowanie, co ułatwia utrzymanie kodu i rozszerzanie go o nowe stany.
Wyzwanie
ŁatwyZbudujmy system sygnalizacji świetlnej z użyciem wzorca Stan. Sygnalizacja świetlna przechodzi cyklicznie przez różne stany (czerwony, żółty, zielony), a każdy stan określa, co się dzieje po zmianie światła. Zamiast używać złożonych instrukcji warunkowych, zamkniesz każdy stan światła w osobnej klasie.
Rozmieścisz kod w trzech plikach:
TrafficLightState.cs: Zdefiniuj interfejsITrafficLightStatew przestrzeni nazwTrafficz metodąChange(TrafficLight light). Następnie utwórz trzy konkretne klasy stanów:RedState— gdy wywołana zostanie metodaChange, wypisujeRed - Stop! Changing to Green...i przełącza światło w stanGreenStateGreenState— wypisujeGreen - Go! Changing to Yellow...i przełącza światło w stanYellowStateYellowState— wypisujeYellow - Caution! Changing to Red...i przełącza światło w stanRedState
TrafficLight.cs: Utwórz klasęTrafficLightw tej samej przestrzeni nazw. To klasa kontekstu, która przechowuje bieżący stan. Domyślnie powinna zaczynać w stanieRedStatei mieć:- metodę
SetState(ITrafficLightState state), której obiekty stanów używają do wywoływania przejść - metodę
Change(), która deleguje działanie do metodyChangebieżącego stanu
- metodę
Program.cs: Połącz wszystko, tworząc obiektTrafficLighti cyklicznie zmieniając stany na podstawie danych wejściowych.
Otrzymasz jedną daną wejściową:
- Liczbę zmian świateł sygnalizacji świetlnej (liczbę całkowitą)
Utwórz obiekt TrafficLight (który zaczyna od światła czerwonego) i wywołaj Change() określoną liczbę razy.
Na przykład jeśli dane wejściowe to 3, wynikiem powinno być:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...Jeśli dane wejściowe to 5, cykl świateł zatoczy pełne koło:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...Zwróć uwagę, że klasa TrafficLight nie zawiera logiki określającej, co oznacza każdy kolor ani co następuje po nim — cała ta wiedza znajduje się w poszczególnych klasach stanów. Każdy stan odpowiada za własne zachowanie i wywołanie przejścia do następnego stanu!
Spróbuj swoich sił
using System;
using Traffic;
class Program
{
public static void Main(String[] args)
{
// Odczytaj liczbę zmian sygnalizacji świetlnej
int n = Convert.ToInt32(Console.ReadLine());
// TODO: Utwórz instancję TrafficLight (domyślnie zaczyna w stanie RedState)
// TODO: Wywołaj metodę Change() n razy za pomocą pętli
}
}
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ętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online