Dlaczego Factory Method jest ważny
Każdy programista Java musi regularnie tworzyć obiekty. Zazwyczaj robisz to przez new Object(), ale co gdy masz kilka podobnych klas i chcesz wybrać właściwą w zależności od sytuacji? Factory Method rozwiązuje ten problem elegancko – działa jak „fabryka” która produkuje właściwy obiekt na zamówienie.
Co się nauczysz:
- Czym jest wzorzec Factory Method i kiedy go używać
- Różnicę między Factory Method a zwykłym konstruktorem
- Jak implementować Factory Method w praktyce
- Praktyczne przykłady z życia codziennego programisty
- Najczęstsze błędy i jak ich unikać
Analogia ze świata rzeczywistego
Podstawowa implementacja Factory Method
Zacznijmy od prostego przykładu z systemem płatności:
// Interfejs produktu public interface PaymentProcessor { void processPayment(double amount); } // Konkretne implementacje public class CreditCardProcessor implements PaymentProcessor { @Override public void processPayment(double amount) { System.out.println("Processing $" + amount + " via Credit Card"); // logika płatności kartą } } public class PayPalProcessor implements PaymentProcessor { @Override public void processPayment(double amount) { System.out.println("Processing $" + amount + " via PayPal"); // logika płatności PayPal } } public class BankTransferProcessor implements PaymentProcessor { @Override public void processPayment(double amount) { System.out.println("Processing $" + amount + " via Bank Transfer"); // logika przelewu bankowego } }
Teraz tworzymy fabrykę:
public class PaymentProcessorFactory { public static PaymentProcessor createProcessor(String type) { switch (type.toLowerCase()) { case "creditcard": return new CreditCardProcessor(); case "paypal": return new PayPalProcessor(); case "banktransfer": return new BankTransferProcessor(); default: throw new IllegalArgumentException("Unknown payment type: " + type); } } }
Użycie Factory Method w praktyce
public class ShoppingCart { public void checkout(String paymentMethod, double totalAmount) { // Tworzymy odpowiedni procesor płatności PaymentProcessor processor = PaymentProcessorFactory.createProcessor(paymentMethod); // Używamy bez wiedzy o konkretnej implementacji processor.processPayment(totalAmount); System.out.println("Order completed successfully!"); } } // Użycie public class Main { public static void main(String[] args) { ShoppingCart cart = new ShoppingCart(); cart.checkout("creditcard", 99.99); // Użyje CreditCardProcessor cart.checkout("paypal", 49.99); // Użyje PayPalProcessor cart.checkout("banktransfer", 199.99); // Użyje BankTransferProcessor } }
Zalety Factory Method
**Główne korzyści:**
– **Elastyczność:** Łatwo dodawać nowe typy bez zmiany istniejącego kodu
– **Enkapsulacja:** Logika tworzenia obiektów jest w jednym miejscu
– **Testowanie:** Można łatwo podmienić implementacje w testach
– **Czytelność:** Kod jasno pokazuje intencję („stwórz payment processor”)
Rozszerzona wersja z enum
public enum PaymentType { CREDIT_CARD, PAYPAL, BANK_TRANSFER } public class ImprovedPaymentFactory { public static PaymentProcessor createProcessor(PaymentType type) { switch (type) { case CREDIT_CARD: return new CreditCardProcessor(); case PAYPAL: return new PayPalProcessor(); case BANK_TRANSFER: return new BankTransferProcessor(); default: throw new IllegalArgumentException("Unsupported payment type: " + type); } } }
Najczęstsze błędy początkujących
Kiedy NIE używać Factory Method
Factory Method może być przesadą gdy:
– Masz tylko jedną klasę do tworzenia
– Logika tworzenia jest bardzo prosta
– Nie przewidujesz dodawania nowych typów
**Alternatywy:**
– Zwykły konstruktor dla prostych przypadków
– Builder pattern dla obiektów z wieloma parametrami
– Dependency Injection w większych aplikacjach
Konstruktor tworzy konkretny obiekt określonej klasy. Factory Method może zwrócić różne obiekty w zależności od parametrów, ale wszystkie implementują ten sam interfejs.
Nie. Factory Method to jedna metoda tworząca obiekty jednego typu. Abstract Factory to interfejs do tworzenia rodzin powiązanych obiektów.
Gdy masz kilka klas implementujących ten sam interfejs i chcesz wybrać właściwą w runtime na podstawie parametrów.
Technicznie tak, ale lepiej rzucić wyjątek lub zwrócić domyślną implementację. Null prowadzi do NullPointerException.
Możesz stworzyć testową implementację fabryki zwracającą mock obiekty, lub przetestować każdy typ produktu osobno.
Przydatne zasoby
🚀 Zadanie dla Ciebie
Stwórz Factory Method dla różnych typów loggerów (ConsoleLogger, FileLogger, DatabaseLogger). Każdy logger powinien implementować interfejs Logger z metodą log(String message). Przetestuj fabrykę tworząc różne typy loggerów i używając ich do zapisywania wiadomości.
Gdzie jeszcze widzisz zastosowanie dla Factory Method w swoich projektach? Podziel się pomysłami w komentarzach!