“This is the first day of my participation in the Gwen Challenge in November. See details of the event: The last Gwen Challenge in 2021”.
Factory Method patterns are also known as Virtual constructors, Virtual Constructor, and Factory Method
💬 intentions
The factory method pattern is a creative design pattern that provides a method to create an object in a parent class, allowing subclasses to determine the type of object to instantiate.
Problems ☹
Suppose you are developing a flow management application. The original version only dealt with trucking, so most of the code was in a class called truck.
After a while, the app became extremely popular. You get about a dozen requests a day from shipping companies that want your app to support maritime logistics.
Adding a new class to your program isn’t that easy if the rest of your code is already coupled to an existing class:
That’s good news. But what about the code problem? Currently, most of the code is related to trucks. Adding the ship class to the program requires modifying all the code. Even worse, if you need to support another mode of transportation in your application later, you will likely need to make significant changes to the code again.
In the end, you’ll have to write a lot of code to handle different things in your application depending on the transport object class.
😊 Solution
The factory method pattern suggests using special factory methods instead of direct calls to object constructors (that is, using the new operator). Don’t worry, the object will still be created with the new operator, just called in the factory method instead. The objects returned by factory methods are often referred to as “products.”
Subclasses can modify the type of object returned by the factory method:
At first glance, this change might seem meaningless: we’re just changing where the constructor is called in the program. But, if you think about it, you can now override the factory method in a subclass to change the type of product it creates.
One caveat: Subclasses can return different types of products only if the products have a common base class or interface, and factory methods in the base class should declare their return type as the common interface.
All products must use the same interface:
For example, both the Truck and Ship classes must implement the Transport interface, which declares a delivery method called Deliver. Each class will implement this approach in a different way: trucks will deliver goods by land, ships by sea. The factory method in the RoadLogistics class returns to the truck object, while the SeaLogistics class returns to the ship object.
As long as the product class implements a common interface, you can pass its objects to the client code without providing additional data:
The code that calls the factory method (often referred to as client code) does not need to know the difference between the actual object returned by different subclasses. Clients treat all products as abstract shipments. The client knows that all transport objects provide delivery methods, but does not care how they are implemented.
🏆 Factory method pattern structure
① The Product will declare the interface. These interfaces are common to all objects built by the creator and his subclasses.
② Concrete Products are different implementations of product interfaces.
③ The Creator class declares a factory method that returns a product object. The return object type of this method must match the product interface. You can declare a factory method as an abstract method, forcing each subclass to implement the method differently. Alternatively, you can return the default product type in the base factory method. Note that although its name is creator, its primary responsibility is not to create products. In general, the creator class contains some core business logic related to the product. The factory approach separates these logical processes from the concrete product classes. For example, large software development companies have programmer training departments. But these companies still write code, not produce programmers.
(4) Concrete Creators will rewrite the basic factory methodology so that it returns a different type of product. Note that a new instance is not necessarily created every time a factory method is called. Factory methods can also return existing objects from the cache, object pool, or other sources.
# pseudocode
The following (cross-platform dialog) example demonstrates how to use the factory approach to develop cross-platform UI (user interface) components while avoiding coupling between client code and specific UI classes.
The base dialog class renders the window using different UI components. These components may look slightly different on different operating systems, but their functionality remains the same. Buttons in Windows are still buttons in Linux.
If you use the factory approach, you don’t need to rewrite the dialog logic for every operating system. If we declare a factory method that generates buttons in the basic dialog class, we can create a dialog subclass that returns Windows style buttons through the factory method. The subclass inherits most of the code from the base class of the dialog box while rendering the buttons on the screen according to Windows style.
For this pattern to work, the base dialog class must use abstract buttons (such as base classes or interfaces) that can be extended to concrete buttons. This way, the code will work no matter what type of button is used in the dialog.
You can use this approach to develop other UI components. However, each time you add a new factory method to the dialog box, you get one step closer to the abstract factory pattern.
The factory method declared by the creator class must return an object of the product class. The creator's subclass usually provides
// The implementation of this method.
class Dialog is// The author can also provide default implementations of some factory methods.abstract method createButton() :Button// Note that the creator's primary responsibility is not to create the product. It usually contains some core business // logic that depends on the product object returned by the factory method. Subclasses can modify the business logic indirectly by overwriting the factory // method and making it return a different type of product.method render(a)is// Call the factory method to create a product object.Button okButton = createButton()
// Use the product now.
okButton.onClick(closeDialog)
okButton.render()
// The concrete creator will override the factory method to change the type of product it returns.
class WindowsDialog extends Dialog is
method createButton() :Button is
return new WindowsButton(a)class WebDialog extends Dialog is
method createButton() :Button is
return new HTMLButton() // Actions that must be implemented by all specific products will be declared in the product interface.interface Button is
method render(a)method onClick(f) // Various implementations of product interfaces should be provided for specific products.class WindowsButton implements Button is
method render(a.b) is/ / according toWindowsStyle render button.method onClick(f) is// Bind the local OS click event.class HTMLButton implements Button is
method render(a.b) is// Return a buttonHTMLFormulated.method onClick(f) is// Bind the web browser click event.class Application is
field dialog: Dialog// The program selects the creator type based on the current configuration or environment Settings.method initialize(a)is
config = readApplicationConfigFile()
if (config.OS == "Windows") then
dialog = new WindowsDialog()
else if (config.OS == "Web") then
dialog = new WebDialog()
else
throw new Exception("Wrong! Unknown operating system.)
// The current client code interacts with the instance of the specific creator, but only through its basic interface
/ /. You can child any creator as long as the client interacts with the creator through the basic interface
// Class passed to the client.
method main(a) is
this.initialize(a)
dialog.render(a)
Copy the code
💡 Factory method mode is suitable for application scenarios
⚡ Use the factory method when you are writing code and cannot predict the exact class of objects and their dependencies.
The factory approach separates the code that creates the product from the code that actually uses the product, making it possible to extend part of the product creation code without affecting the rest of the code.
For example, if you need to add a new product to your application, you can simply develop a new creator subclass and override its factory method.
⚡ If you want users to extend internal components of your software library or framework, use the factory method.
⚡ If you want to save system resources by reusing existing objects rather than recreating them each time, use the factory approach.
This resource requirement is often encountered when dealing with large resource-intensive objects, such as database connections, file systems, and network resources.
Let’s think about ways to reuse existing objects:
- First, you need to create storage space to hold all the objects you have created.
- When someone requests an object, the program searches the object pool for available objects.
- … It is then returned to the client code.
- If no object is available, the program creates a new object (and adds it to the object pool).
Probably the most obvious and convenient way is to put this code in the constructor of the object class we are trying to reuse. But by definition, a constructor always returns a new object; it cannot return an existing instance.
Therefore, you need a common method that can create new objects and reuse existing ones. That sounds very similar to the factory approach.
📕 Implementation
-
Make all products follow the same interface. The interface must declare methods that make sense for all products.
-
Add an empty factory method to the create class. The return type of this method must follow the common product interface.
-
Find all references to the product constructor in the creator code. Replace them in turn with calls to the factory method, moving the code that creates the product into the factory method. You may need to add temporary parameters to the factory method to control the type of product returned.
The code for the factory method can look pretty bad. There may be complex switch branch operators for selecting various product classes to instantiate. But don’t worry, we will fix this problem soon.
-
Now, write a creator subclass for each product in the factory method, then override the factory method in the subclass and move the associated creation code from the base method to the factory method.
-
If there are too many product types in your application, it is not necessary to subclass each product, and you can reuse the control parameters from the base class in the subclass.
For example, imagine you have some of the following hierarchies of classes. Base mail and its subcategories air mail and land mail; Transport and its subcategories aircraft, trucks and trains. Air mail uses only airplane objects, while land mail uses both truck and train objects. You could write a new subclass (such as train mail) to handle both cases, but there are other alternatives. The client code can pass a parameter to the overland mail class to control which products it wants to obtain.
-
If there is no code left in the base factory method after the above move, you can turn it into an abstract class. If there are additional statements in the underlying factory method, you can set them as the default behavior for that method.
⚖ Advantages and disadvantages of the factory method pattern
Advantages ✔
- Tight coupling between creators and specific products can be avoided.
- Single responsibility principle. You can put product creation code in a single location in your application, making it easier to maintain.
- Open and close principle. You can introduce new product types into your application without changing existing client code.
Faults ❌
- Applying the factory method pattern requires the introduction of many new subclasses, which can make the code more complex. The best case scenario is to introduce this pattern into an existing hierarchy of creator classes.
🌹Java code example
Application of factory pattern in Java API
Usage example: The factory method pattern is widely used in Java code. This pattern is useful when you need to provide a high level of flexibility in your code.
Core Java libraries have applications of this pattern:
java.util.Calendar#getInstance()
java.util.ResourceBundle#getBundle()
java.text.NumberFormat#getInstance()
java.nio.charset.Charset#forName()
- Java.net.URLStreamHandlerFactory#createURLStreamHandler (String) (according to the agreement to return different singleton)
java.util.EnumSet#of()
- Javax.mail. XML. Bind. JAXBContext# createMarshaller () and other similar methods.
Identification methods: Factory methods can be identified by building methods that create objects of concrete classes but return them as abstract types or interfaces.
Generate cross-platform GUI elements
In this case, the button is the product and the dialog box is the creator.
Different types of dialog boxes require elements of their own type. So we can subclass each dialog box type and override its factory method.
Each dialog box type will now initialize the appropriate button class. The dialog base class uses its generic interface to interact with objects, so code changes can still work.
Bag buttons
Buttons/button.java: Generic product interface
package refactoring_guru.factory_method.example.buttons;
/** * Common interface for all buttons. */
public interface Button {
void render(a);
void onClick(a);
}
Copy the code
Buttons/htmlbutton. Java: Specific product
package refactoring_guru.factory_method.example.buttons;
/** * HTML button implementation. */
public class HtmlButton implements Button {
public void render(a) {
System.out.println("<button>Test Button</button>");
onClick();
}
public void onClick(a) {
System.out.println("Click! Button says - 'Hello World! '"); }}Copy the code
Buttons/WindowsButton. Java: another specific products
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(a) {
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(a) {
button = new JButton("Exit");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.setVisible(false);
System.exit(0); }}); }}Copy the code
The factory package
Factory/dialog.java: Basic creator
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(a) {
// ... other code ...
Button okButton = createButton();
okButton.render();
}
/** * Subclasses will override this method in order to create specific button * objects. */
public abstract Button createButton(a);
}
Copy the code
Factory/htmldialog.java: Specific creator
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(a) {
return newHtmlButton(); }}Copy the code
The factory/WindowsDialog. Java: another creator
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(a) {
return newWindowsButton(); }}Copy the code
Demo.java: client code
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(a) {
if (System.getProperty("os.name").equals("Windows 10")) {
dialog = new WindowsDialog();
} else {
dialog = newHtmlDialog(); }}/** * 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(a) { dialog.renderWindow(); }}Copy the code
Outputdemo.txt: Execution result (HtmlDialog)
<button>Test Button</button>
Click! Button says - 'Hello World! '
Copy the code
Outputdemo.png: execution result (WindowsDialog)
🏆 You’ve got nothing to lose!