工厂方法模式
前言
什么是工廠方法
工廠方法 是一種 創建型 設計模式
什么是 創建型 設計模式?
創建型設計模式專注于處理對象創建機制,以合適的方式來創建對象。該模式通過控制對象的創建方式來解決問題。
工廠方法的作用
解決了在 不指定具體類 的情況下創建產品對象的問題,這句話要怎么理解呢?
工廠方法模式通過讓子類決定該創建的對象是什么,來達到將對象創建的過程封裝的目的。
工廠方法定義了一個方法,且必須使用該方法代替通過直接調用構造函數來創建對象(new 操作符)的方式。
這個怎么理解呢?
工廠方法模式將對象的創建委托給子類,子類實現工廠方法來創建對象。
子類可重寫該方法來更改將被創建的對象所屬類。
示例
這里就以生成跨平臺的 GUI 元素為例子,來說明工廠方法模式的使用。
在本例中,按鈕擔任產品的角色,對話框擔任創建者的角色。
不同類型的對話框需要其各自類型的元素。因此我們可為每個對話框類型創建子類并重寫其工廠方法。
現在,每種對話框類型都將對合適的按鈕類進行初始化。對話框基類使用其通用接口與對象進行交互,因此代碼更改后仍能正常工作。
buttons
buttons/Button.java: 通用產品接口
/**
* @author BNTang
* @version 1.0
* @description 通用產品接口
* @since 2023-11-23 23:10:54
**/
public interface Button {
/**
* 渲染
*/
void render();
/**
* 點擊
*/
void onClick();
}
Html Button 產品
buttons/HtmlButton.java: 具體產品
/**
* @author BNTang
* @version 1.0
* @description HTML按鈕
* @since 2023-11-23 23:10:54
**/
public class HtmlButton implements Button {
@Override
public void render() {
System.out.println("<button>Test Button</button>");
onClick();
}
@Override
public void onClick() {
System.out.println("Click! Button says - 'Hello World!'");
}
}
Windows Button 產品
buttons/WindowsButton.java: windows 按鈕產品
/**
* @author BNTang
* @version 1.0
* @description windows按鈕
* @since 2023-11-23 23:10:54
**/
public class WindowsButton implements Button {
JPanel panel = new JPanel();
JFrame frame = new JFrame();
JButton button;
@Override
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();
}
@Override
public void onClick() {
button = new JButton("Exit");
button.addActionListener(e -> {
frame.setVisible(false);
System.exit(0);
});
}
}
factory
factory/Dialog.java: 創建者
/**
* @author BNTang
* @version 1.0
* @description 基本工廠類。請注意,"工廠 "只是該類的一個角色。它
* 應該有一些需要創建不同產品的核心業務邏輯。
* @since 2023-11-23 23:14:36
**/
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();
}
Html Dialog 創建者
factory/HtmlDialog.java: Html Dialog 具體創建者
/**
* @author BNTang
* @version 1.0
* @description HTML 對話框將生成 HTML 按鈕。
* @since 2023-11-23 23:14:36
**/
public class HtmlDialog extends Dialog {
@Override
public Button createButton() {
return new HtmlButton();
}
}
Windows Dialog 創建者
factory/WindowsDialog.java: Windows Dialog 具體創建者
/**
* @author BNTang
* @version 1.0
* @description Windows 對話框將生成 Windows 按鈕。
* @since 2023-11-23 23:16:45
**/
public class WindowsDialog extends Dialog {
@Override
public Button createButton() {
return new WindowsButton();
}
}
客戶端代碼
Demo.java: 客戶端代碼
/**
* @author BNTang
* @version 1.0
* @description
* @since 2023-11-23 23:17:38
**/
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();
}
}
測試
因為我目前電腦是 windows 所以通過 configure() 方法選擇了 WindowsDialog,然后運行 runBusinessLogic() 方法,最終輸出了 windows 的按鈕。
如上的過程就是工廠方法模式的使用過程。我們再來進一步更加深刻的理解一下工廠方法模式。
意圖
工廠方法模式是一種創建型設計模式,其在父類中提供一個創建對象的方法,允許子類決定實例化對象的類型。
例如在我們現實生活當中,有物流公司,有很多種物流公司,那么通過如上介紹的工廠方法模式,我們可以將物流公司抽象成一個父類,然后子類繼承父類,然后子類實現父類的抽象方法,這樣就可以實現不同的物流公司,來實現不同的物流方式。
問題
假設你正在開發一款物流管理應用。最初版本只能處理卡車運輸,因此大部分代碼都在位于名為 卡車 的類中。
一段時間后,這款應用變得極受歡迎。你每天都能收到十幾次來自海運公司的請求,希望應用能夠支持海上物流功能。
如果代碼其余部分與現有類已經存在耦合關系,那么向程序中添加新類其實并沒有那么容易。
這可是個好消息。但是代碼問題該如何處理呢?目前,大部分代碼都與 卡車 類相關。在程序中添加 輪船 類需要修改全部代碼。
更糟糕的是,如果你以后需要在程序中支持另外一種運輸方式,很可能需要再次對這些代碼進行大幅修改。
最后,你將不得不編寫繁復的代碼,根據不同的運輸對象類,在應用中進行不同的處理。
解決方案
工廠方法模式建議使用特殊的工廠方法代替對于對象構造函數的直接調用(即使用 new 運算符)。
不用擔心,對象仍將通過 new 運算符創建,只是該運算符改在工廠方法中調用罷了。工廠方法返回的對象通常被稱作 “產品”。
子類可以修改工廠方法返回的對象類型。
乍看之下,這種更改可能毫無意義: 我們只是改變了程序中調用構造函數的位置而已。但是,仔細想一下,現在你可以在子類中重寫工廠方法,從而改變其創建產品的類型。
但有一點需要注意: 僅當這些產品具有共同的基類或者接口時,子類才能返回不同類型的產品,同時基類中的工廠方法還應將其返回類型聲明為這一共有接口。
所有產品都必須使用同一接口。
舉例來說, 卡車Truck和 輪船Ship類都必須實現 運輸Transport接口, 該接口聲明了一個名為 deliver交付的方法。每個類都將以不同的方式實現該方法:卡車走陸路交付貨物, 輪船走海路交付貨物。 陸路運輸Road-Logistics類中的工廠方法返回卡車對象,而 海路運輸Sea-Logistics類則返回輪船對象。
只要產品類實現一個共同的接口, 你就可以將其對象傳遞給客戶代碼, 而無需提供額外數據。
調用工廠方法的代碼 (通常被稱為客戶端代碼) 無需了解不同子類返回實際對象之間的差別。 客戶端將所有產品視為抽象的 運輸 。 客戶端知道所有運輸對象都提供 交付方法, 但是并不關心其具體實現方式。
最后
- 參考資料:https://refactoringguru.cn/design-patterns/factory-method/java/example
總結
- 上一篇: 关于点赞业务对MySQL和Redis和M
- 下一篇: springBoot + 工厂模式 实现