Wzorzec Factory
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 66 z 87.
Wzorzec fabryki to kreacyjny wzorzec projektowy, który przekazuje tworzenie obiektów osobnej metodzie lub klasie. Zamiast używać bezpośrednio new w swoim kodzie, prosisz fabrykę o utworzenie obiektów. Jest to szczególnie przydatne, gdy masz wiele powiązanych klas współdzielących wspólny interfejs lub klasę nadrzędną.
Rozważ system powiadomień, który może wysyłać e-maile, SMS-y lub powiadomienia push. Bez fabryki kod musiałby znać każdą konkretną klasę:
// Bez fabryki — kod klienta musi znać wszystkie klasy konkretne
Notification notification;
if (type.equals("email")) {
notification = new EmailNotification();
} else if (type.equals("sms")) {
notification = new SMSNotification();
}Wzorzec Fabryka pozwala scentralizować tę logikę tworzenia:
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
public void send(String message) {
System.out.println("Email: " + message);
}
}
public class SMSNotification implements Notification {
public void send(String message) {
System.out.println("SMS: " + message);
}
}
public class NotificationFactory {
public static Notification create(String type) {
if (type.equals("email")) {
return new EmailNotification();
} else if (type.equals("sms")) {
return new SMSNotification();
}
return null;
}
}Teraz kod klienta jest czytelniejszy i nie zależy od konkretnych klas:
Notification notification = NotificationFactory.create("email");
notification.send("Hello!"); // Wynik: Email: Hello!Główną zaletą jest to, że dodanie nowych typów powiadomień wymaga aktualizacji tylko fabryki: kod klienta pozostaje bez zmian. Jest to zgodne z zasadą programowania do interfejsu, dzięki czemu system jest bardziej elastyczny i łatwiejszy do rozszerzania.
Wyzwanie
ŁatwyZbudujmy system rysowania kształtów, używając wzorca Factory! Zamiast tworzyć obiekty kształtów bezpośrednio w różnych miejscach kodu, scentralizujesz logikę ich tworzenia w fabryce, która tworzy różne typy kształtów na podstawie prostego identyfikatora tekstowego.
Rozmieścisz kod w czterech plikach:
Shape.java: Zdefiniuj interfejs o nazwieShape, który będzie wspólnym kontraktem dla wszystkich kształtów. Twój interfejs powinien deklarować jedną metodędraw(), która nic nie zwraca, ale wyświetla informacje o rysowanym kształcie.Shapes.java: Utwórz trzy klasy implementujące interfejsShape:Circle— jego metodadraw()powinna wyświetlaćDrawing a CircleRectangle— jego metodadraw()powinna wyświetlaćDrawing a RectangleTriangle— jego metodadraw()powinna wyświetlaćDrawing a TriangleShapeFactory.java: Utwórz klasę fabryki, która obsługuje tworzenie kształtów. KlasaShapeFactorypowinna mieć metodę statycznącreateShape(String type), która zwraca obiekt typuShape. W zależności od parametru type:- Jeśli type jest równe
"circle", zwróć nowy obiekt klasyCircle - Jeśli type jest równe
"rectangle", zwróć nowy obiekt klasyRectangle - Jeśli type jest równe
"triangle", zwróć nowy obiekt klasyTriangle - Dla każdej innej wartości zwróć
null
- Jeśli type jest równe
Main.java: Połącz wszystkie elementy systemu fabryki! Otrzymasz dwa wejścia: dwa typy kształtów (oba typu String).Dla każdego wejścia użyj klasy
ShapeFactory, aby utworzyć kształt i zapisać go w zmiennej typuShape. Jeśli fabryka zwróci prawidłowy kształt (inny niż null), wywołaj jego metodędraw(). Jeśli zwróci null, wyświetlUnknown shape: [type], gdzie [type] to przekazane wejście.Przetwórz oba wejścia po kolei, wyświetlając wynik każdego z nich w osobnym wierszu.
Otrzymasz po kolei dwa wejścia: typ pierwszego kształtu (String) i typ drugiego kształtu (String).
Zwróć uwagę, że Twoja klasa Main nigdy nie używa bezpośrednio new Circle() ani new Rectangle(). Zna jedynie interfejs Shape i prosi fabrykę o utworzenie obiektów. Na tym polega siła wzorca Factory: kod klienta jest niezależny od konkretnych klas!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Wczytaj dwa typy figur
String type1 = scanner.nextLine();
String type2 = scanner.nextLine();
// TODO: Użyj ShapeFactory, aby utworzyć pierwszą figurę
// Jeśli figura nie jest null, wywołaj draw()
// Jeśli figura jest null, wypisz "Unknown shape: [type]"
// TODO: Zrób to samo dla drugiej figury
}
}
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ściPoćwicz samodzielnie: Kompilator Java online