Wzorzec polecenia
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 70 z 87.
Wzorzec Polecenia to behawioralny wzorzec projektowy, który przekształca żądanie w samodzielny obiekt zawierający wszystkie informacje o tym żądaniu. Dzięki tej transformacji możesz przekazywać żądania jako argumenty metod, opóźniać ich wykonanie lub umieszczać je w kolejce, a także obsługiwać operacje, które można cofnąć.
Pomyśl o tym jak o restauracji: zamiast mówić kucharzowi bezpośrednio, co ma ugotować, zapisujesz zamówienie na kartce. Kelner (wywołujący) zanosi kartkę (polecenie) do kuchni, gdzie można ją umieścić w kolejce, nadać jej priorytet, a nawet anulować. Ten wzorzec oddziela obiekt wywołujący operację od obiektu, który ją wykonuje.
Wzorzec ma cztery kluczowe elementy: interfejs Command deklarujący metodę execute, klasy ConcreteCommand implementujące konkretne działania, obiekt Receiver wykonujący właściwą pracę oraz obiekt Invoker wywołujący polecenia:
interface Command {
void execute();
}class Light { // Odbiornik
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}class RemoteControl { // Wywołujący
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}Wywołujący nie wie, jakie działanie zostanie wykonane. Po prostu wywołuje execute():
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // Wynik: Light is ONWzorzec polecenia sprawdza się doskonale, gdy trzeba kolejkować operacje, zaimplementować funkcję cofania i ponawiania lub zaplanować zadania do późniejszego wykonania. Każde polecenie zawiera wszystko, co jest potrzebne do wykonania działania, dzięki czemu system staje się bardziej elastyczny i rozszerzalny.
Wyzwanie
ŁatwyZbudujmy system automatyki inteligentnego domu z użyciem wzorca polecenia! Stworzysz system, w którym różnymi urządzeniami domowymi można sterować za pomocą poleceń, co pozwala kolejkować i wykonywać działania, a nawet przechowywać je do późniejszego użycia — podobnie jak w przypadku uniwersalnego pilota, który nie musi wiedzieć, jak działa każde urządzenie.
Rozmieścisz kod w czterech plikach:
Command.java: Zdefiniuj interfejsCommand, który będą implementować wszystkie polecenia urządzeń. Powinien deklarować jedną metodęexecute(), która wykonuje działanie polecenia.Devices.java: Utwórz dwie klasy odbiorników, które reprezentują urządzenia inteligentnego domu:Television— ma metodypowerOn(), która wypisujeTV is now ON,powerOff(), która wypisujeTV is now OFF, orazsetChannel(int channel), która wypisujeTV channel set to [channel].Thermostat— ma metodysetTemperature(int temp), która wypisujeThermostat set to [temp] degrees, orazturnOff(), która wypisujeThermostat turned OFF.Commands.java: Utwórz konkretne klasy poleceń, które hermetyzują operacje na urządzeniach:TVOnCommand— przyjmuje obiektTelevisionw konstruktorze i podczas wykonania wywołujepowerOn().TVOffCommand— przyjmuje obiektTelevisionw konstruktorze i podczas wykonania wywołujepowerOff().TVChannelCommand— przyjmuje obiektTelevisioni wartośćint channelw konstruktorze, a podczas wykonania wywołujesetChannel()z tym kanałem.ThermostatSetCommand— przyjmuje obiektThermostati wartośćint temperaturew konstruktorze, a podczas wykonania wywołujesetTemperature()z tą wartością.Main.java: Utwórz klasęRemoteControl(wywołujący) z metodąsetCommand(Command cmd), która zapisuje polecenie, oraz metodąpressButton(), która wykonuje zapisane polecenie.W metodzie main otrzymasz dwa dane wejściowe: numer kanału telewizora (liczbę całkowitą) oraz temperaturę termostatu (liczbę całkowitą).
Utwórz obiekt
Televisioni obiektThermostat. Utwórz obiektRemoteControli zademonstruj wzorzec polecenia, wykonując następujące czynności:- Ustaw
TVOnCommandi naciśnij przycisk - Ustaw
TVChannelCommandz podanym numerem kanału i naciśnij przycisk - Ustaw
ThermostatSetCommandz podaną temperaturą i naciśnij przycisk - Ustaw
TVOffCommandi naciśnij przycisk
- Ustaw
Otrzymasz kolejno dwa dane wejściowe: kanał telewizora (liczbę całkowitą) i temperaturę termostatu (liczbę całkowitą).
Na przykład dla danych wejściowych 5 i 72 wynik będzie następujący:
TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFFZauważ, że RemoteControl nigdy nie komunikuje się bezpośrednio z urządzeniami. Zna jedynie interfejs Command. Każde polecenie hermetyzuje wszystko, co jest potrzebne do wykonania jego działania, dzięki czemu można łatwo dodawać nowe urządzenia i polecenia bez zmieniania wywołującego!
Spróbuj swoich sił
import java.util.Scanner;
// TODO: Utwórz klasę RemoteControl (wywołującą) z:
// - metodą setCommand(Command cmd) do przechowywania polecenia
// - metodą pressButton() do wykonania zapisanego polecenia
class RemoteControl {
// TODO: Zaimplementuj RemoteControl
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Odczytaj dane wejściowe
int channel = scanner.nextInt();
int temperature = scanner.nextInt();
// TODO: Utwórz Television i Thermostat
// TODO: Utwórz RemoteControl
// TODO: Zademonstruj wzorzec Command:
// 1. Ustaw TVOnCommand i naciśnij przycisk
// 2. Ustaw TVChannelCommand z kanałem podanym na wejściu i naciśnij przycisk
// 3. Ustaw ThermostatSetCommand z temperaturą podaną na wejściu i naciśnij przycisk
// 4. Ustaw TVOffCommand i naciśnij przycisk
scanner.close();
}
}
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ętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatności9Typy generyczne
Wprowadzenie do typów generycznychKlasy generyczneMetody generyczneOgraniczone parametry typówSymbole wieloznaczne (?, extends, super)Powtórzenie – kontener generyczny12Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec iteratoraPoćwicz samodzielnie: Kompilator Java online