
Metoda wytwórcza w języku Java
Metoda wytwórcza jest kreacyjnym wzorcem projektowym rozwiązującym problem tworzenia obiektów-produktów bez określania ich konkretnych klas.
Metoda wytwórcza definiuje metodę która ma służyć tworzeniu obiektów bez bezpośredniego wywoływania konstruktora (poprzez operator new
). Podklasy mogą nadpisać tę metodę w celu zmiany klasy tworzonych obiektów.
Jeśli masz problem ze zrozumieniem różnicy pomiędzy poszczególnymi koncepcjami i wzorcami wytwórczymi, przeczytaj nasze Porównanie fabryk.
Złożoność:
Popularność:
Przykłady użycia: Wzorzec Metoda wytwórcza jest szeroko rozpowszechniony w kodzie Java. Przydaje się gdy trzeba wyposażyć kod w wysoki poziom elastyczności.
Wzorzec jest obecny w głównych bibliotekach Java:
java.util.Calendar#getInstance()
java.util.ResourceBundle#getBundle()
java.text.NumberFormat#getInstance()
java.nio.charset.Charset#forName()
java.net.URLStreamHandlerFactory#createURLStreamHandler(String)
(Zwraca różne obiekty singleton, zależnie od protokołu)java.util.EnumSet#of()
javax.xml.bind.JAXBContext#createMarshaller()
i inne podobne metody.
Identyfikacja: Metody wytwórcze można rozpoznać po metodach kreacyjnych tworzących obiekty na podstawie konkretnych klas, ale zwracających typ abstrakcyjny lub interfejs.
Tworzenie międzyplatformowych elementów graficznego interfejsu użytkownika (GUI)
W poniższym przykładzie przyciski odgrywają rolę produktów, a okna dialogowe — twórców.
Różne rodzaje okien dialogowych wymagają własnych typów elementów. Dlatego na każde okno dialogowe tworzymy podklasę i nadpisujemy jej metody wytwórcze.
W rezultacie każdy rodzaj okna dialogowego stworzy instancje odpowiednich klas przycisków. Klasa bazowa okna dialogowego współdziała z produktami za pośrednictwem ich wspólnego interfejsu, więc jej kod będzie działał nawet po wszystkich zmianach.
buttons
buttons/Button.java: Wspólny interfejs produktu
package refactoring_guru.factory_method.example.buttons; /** * Common interface for all buttons. */ public interface Button { void render(); void onClick(); }
buttons/HtmlButton.java: Konkretny produkt
package refactoring_guru.factory_method.example.buttons; /** * HTML button implementation. */ public class HtmlButton implements Button { public void render() { System.out.println("<button>Test Button</button>"); onClick(); } public void onClick() { System.out.println("Click! Button says - 'Hello World!'"); } }
buttons/WindowsButton.java: Kolejny konkretny produkt
package refactoring_guru.factory_method.example.buttons; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * Windows button implementation. */ public class WindowsButton implements Button { JPanel panel = new JPanel(); JFrame frame = new JFrame(); JButton button; public void render() { frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JLabel label = new JLabel("Hello World!"); label.setOpaque(true); label.setBackground(new Color(235, 233, 126)); label.setFont(new Font("Dialog", Font.BOLD, 44)); label.setHorizontalAlignment(SwingConstants.CENTER); panel.setLayout(new FlowLayout(FlowLayout.CENTER)); frame.getContentPane().add(panel); panel.add(label); onClick(); panel.add(button); frame.setSize(320, 200); frame.setVisible(true); onClick(); } public void onClick() { button = new JButton("Exit"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { frame.setVisible(false); System.exit(0); } }); } }
factory
factory/Dialog.java: Bazowy twórca
package refactoring_guru.factory_method.example.factory; import refactoring_guru.factory_method.example.buttons.Button; /** * Base factory class. Note that "factory" is merely a role for the class. It * should have some core business logic which needs different products to be * created. */ public abstract class Dialog { public void renderWindow() { // ... other code ... Button okButton = createButton(); okButton.render(); } /** * Subclasses will override this method in order to create specific button * objects. */ public abstract Button createButton(); }
factory/HtmlDialog.java: Konkretny twórca
package refactoring_guru.factory_method.example.factory; import refactoring_guru.factory_method.example.buttons.Button; import refactoring_guru.factory_method.example.buttons.HtmlButton; /** * HTML Dialog will produce HTML buttons. */ public class HtmlDialog extends Dialog { @Override public Button createButton() { return new HtmlButton(); } }
factory/WindowsDialog.java: Kolejny konkretny twórca
package refactoring_guru.factory_method.example.factory; import refactoring_guru.factory_method.example.buttons.Button; import refactoring_guru.factory_method.example.buttons.WindowsButton; /** * Windows Dialog will produce Windows buttons. */ public class WindowsDialog extends Dialog { @Override public Button createButton() { return new WindowsButton(); } }
Demo.java: Kod klienta
package refactoring_guru.factory_method.example; import refactoring_guru.factory_method.example.factory.Dialog; import refactoring_guru.factory_method.example.factory.HtmlDialog; import refactoring_guru.factory_method.example.factory.WindowsDialog; /** * Demo class. Everything comes together here. */ public class Demo { private static Dialog dialog; public static void main(String[] args) { configure(); runBusinessLogic(); } /** * The concrete factory is usually chosen depending on configuration or * environment options. */ static void configure() { if (System.getProperty("os.name").equals("Windows 10")) { dialog = new WindowsDialog(); } else { dialog = new HtmlDialog(); } } /** * All of the client code should work with factories and products through * abstract interfaces. This way it does not care which factory it works * with and what kind of product it returns. */ static void runBusinessLogic() { dialog.renderWindow(); } }
OutputDemo.txt: Wyniki działania (HtmlDialog)
<button>Test Button</button> Click! Button says - 'Hello World!'
OutputDemo.png: Wyniki działania (WindowsDialog)
