Wzorzec adaptera
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 71 z 87.
Wzorzec adaptera to strukturalny wzorzec projektowy, który umożliwia współpracę niezgodnych interfejsów. Działa jak pomost między dwiema klasami, przekształcając interfejs jednej klasy w interfejs, którego oczekuje klient. Pomyśl o nim jak o adapterze zasilania, który pozwala podłączyć europejskie urządzenie do amerykańskiego gniazdka.
Wyobraź sobie, że masz istniejący interfejs MediaPlayer, który odtwarza pliki audio, ale musisz zintegrować bibliotekę innej firmy, która używa zupełnie innego interfejsu do odtwarzania filmów:
// Istniejący interfejs używany przez twój kod
interface MediaPlayer {
void play(String filename);
}
// Klasa zewnętrzna z niezgodnym interfejsem
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}Adapter opakowuje niekompatybilną klasę i implementuje oczekiwany interfejs, tłumacząc wywołania między nimi:
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}Teraz Twój kod kliencki może korzystać z odtwarzacza wideo za pośrednictwem znanego interfejsu MediaPlayer:
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // Wynik: Odtwarzanie wideo: movie.mp4Wzorzec Adapter jest nieoceniony przy integrowaniu starszego kodu lub bibliotek innych firm bez modyfikowania ich kodu źródłowego. Klient nie wie, że współpracuje z klasą-adapterem. Po prostu korzysta ze znanego mu interfejsu.
Wyzwanie
ŁatwyZbudujmy system konwersji temperatury, korzystając ze wzorca Adapter! Masz istniejącą aplikację pogodową, która obsługuje temperatury w stopniach Celsjusza, ale musisz zintegrować starszy czujnik temperatury, który podaje odczyty wyłącznie w stopniach Fahrenheita. Zamiast modyfikować którykolwiek z systemów, utworzysz adapter, który połączy je ze sobą.
Rozmieścisz swój kod w czterech plikach:
TemperatureProvider.java: Zdefiniuj interfejs, którego oczekuje Twoja aplikacja pogodowa. Powinien deklarować metodęgetTemperatureCelsius(), która zwraca wartość typu double reprezentującą temperaturę w stopniach Celsjusza.FahrenheitSensor.java: Ta klasa reprezentuje starszy czujnik z niezgodnym interfejsem. Utwórz klasę z prywatnym polem typu double przechowującym temperaturę w stopniach Fahrenheita, ustawianym przez konstruktor. Klasa powinna mieć metodęreadFahrenheit(), która zwraca zapisaną wartość temperatury w stopniach Fahrenheita.SensorAdapter.java: Utwórz adapter, który dostosuje czujnik Fahrenheita do współpracy z Twoją aplikacją pogodową. Adapter powinien implementować interfejsTemperatureProvideri opakowywać obiektFahrenheitSensor. Po wywołaniu metodygetTemperatureCelsius()powinien odczytać wartość z czujnika i przeliczyć ją na stopnie Celsjusza za pomocą wzoru:(fahrenheit - 32) * 5 / 9.Main.java: Połącz wszystko w całość! Otrzymasz jedno wejście: odczyt temperatury w stopniach Fahrenheita (double).Utwórz obiekt
FahrenheitSensorz wartością wejściową. Następnie utwórz obiektSensorAdapter, który opakowuje ten czujnik. Przypisz adapter do zmiennej typuTemperatureProvider, aby pokazać, że Twoja aplikacja pogodowa może z niego korzystać za pośrednictwem oczekiwanego interfejsu.Wypisz wynik w tym formacie:
Temperature: [celsius] C, gdzie wartość celsius jest sformatowana do jednego miejsca po przecinku.
Otrzymasz jedno wejście: temperaturę w stopniach Fahrenheita (double).
Na przykład dla wejścia 98.6 wynikiem będzie:
Temperature: 37.0 CZwróć uwagę, że Twoja klasa Main korzysta wyłącznie z interfejsu TemperatureProvider. Nie wie, że rzeczywista temperatura pochodzi z czujnika Fahrenheita, dostosowanego w tle. Na tym polega elegancja wzorca Adapter!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double fahrenheit = scanner.nextDouble();
// TODO: Utwórz FahrenheitSensor z podaną wartością wejściową
// TODO: Utwórz SensorAdapter, który opakowuje czujnik
// TODO: Zapisz adapter w zmiennej typu TemperatureProvider
// TODO: Pobierz temperaturę w stopniach Celsjusza i ją wypisz
// Format: "Temperature: [celsius] C" z jednym miejscem po przecinku
// Wskazówka: Użyj String.format("%.1f", value) do formatowania
}
}
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