**工廠方法模式**
是一種創建型設計模式,其在父類中提供一個創建對象的方法,允許子類決定實例化對象的類型。
*****
假設你正在開發一款物流管理應用。最初版本只能處理卡車 運輸,因此大部分代碼都在位于名為 卡車 的類中。 一段時間后,這款應用變得極受歡迎。你每天都能收到十幾 次來自海運公司的請求,希望應用能夠支持海上物流功能。

如果代碼其余部分與現有類已經存在耦合關系,那么向程序 中添加新類其實并沒有那么容易。
這可是個好消息。但是代碼問題該如何處理呢?目前,大部 分代碼都與 卡車 類相關。在程序中添加 輪船 類需要修改 全部代碼。更糟糕的是,如果你以后需要在程序中支持另外 一種運輸方式,很可能需要再次對這些代碼進行大幅修改。
最后,你將不得不編寫繁復的代碼,根據不同的運輸對象類, 在應用中進行不同的處理。
*****
**解決方案**
工廠方法模式建議使用特殊的工廠方法代替對于對象構造函 數的直接調用(即使用 new 運算符)。不用擔心,對象仍將 通過 new 運算符創建,只是該運算符改在工廠方法中調用 罷了。工廠方法返回的對象通常被稱作“產品”。

子類可以修改工廠方法返回的對象類型。
乍看之下,這種更改可能毫無意義:我們只是改變了程序中 調用構造函數的位置而已。但是,仔細想一下,現在你可以 在子類中重寫工廠方法,從而改變其創建產品的類型。
但有一點需要注意:僅當這些產品具有共同的基類或者接口 時,子類才能返回不同類型的產品,同時基類中的工廠方法 還應將其返回類型聲明為這一共有接口。

所有產品都必須使用同一接口。
舉例來說, 卡車 Truck 和 輪船 Ship 類都必須實現 運輸 Transport 接口, 該接口聲明了一個名為 deliver 交 付 的 方 法。 每 個 類 都 將 以 不 同 的 方 式 實 現 該 方 法: 卡 車 走 陸 路 交 付 貨 物, 輪 船 走 海 路 交 付 貨 物。 陸路運輸 RoadLogistics 類中的工廠方法返回卡車對象,而 海路運輸 SeaLogistics 類則返回輪船對象。

調用工廠方法的代碼(通常被稱為客戶端代碼)無需了解不 同子類返回實際對象之間的差別。客戶端將所有產品視為抽 象的 運輸 。 客戶端知道所有運輸對象都提供 交付 方法, 但是并不關心其具體實現方式。
*****

1. 產品(Product)將會對接口進行聲明。對于所有由創建者及其子類構建的對象,這些接口都是通用的。
2. 具體產品(Concrete Products)是產品接口的不同實現。
3. 創建者(Creator)類聲明返回產品對象的工廠方法。該方法的返回對象類型必須與產品接口相匹配。
你可以將工廠方法聲明為抽象方法,強制要求每個子類以不同方式實現該方法。或者,你也可以在基礎工廠方法中返回默認產品類型。
注意,盡管它的名字是創建者,但他最主要的職責并不是創建產品。一般來說,創建者類包含一些與產品相關的核心業務邏輯。
工廠方法將這些邏輯處理從具體產品類中分離出來。打個比方,大型軟件開發公司擁有程序員培訓部門。但是,這些公司的主要工作還是編寫代碼,而非生產程序員。
4. 具體創建者(Concrete Creators) 將會重寫基礎工廠方法,使其返回不同類型的產品。注意,并不一定每次調用工廠方法都會創建新的實例。工廠
方法也可以返回緩存、對象池或其他來源的已有對象。
*****
### **代碼示例**
JAVA版:
## 生成跨平臺的 GUI 元素
在本例中,按鈕擔任產品的角色,對話框擔任創建者的角色。
不同類型的對話框需要其各自類型的元素。因此我們可為每個對話框類型創建子類并重寫其工廠方法。
現在,每種對話框類型都將對合適的按鈕類進行初始化。對話框基類使用其通用接口與對象進行交互,因此代碼更改后仍能正常工作。
buttons/Button.java: 通用產品接口
```
package design_pattern.factory_method.example.buttons;
/**
* Common interface for all buttons.
*/
public interface Button {
void render();
void onClick();
}
```
buttons/HtmlButton.java: 具體產品
```
package design_pattern.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: 另一個具體產品
```
package design_pattern.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/Dialog.java: 基礎創建者
```
package design_pattern.factory_method.example.factory;
import design_pattern.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: 具體創建者
```
package design_pattern.factory_method.example.factory;
import design_pattern.factory_method.example.buttons.Button;
import design_pattern.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: 另一個具體創建者
```
package design_pattern.factory_method.example.factory;
import design_pattern.factory_method.example.buttons.Button;
import design_pattern.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: 客戶端代碼
```
package design_pattern.factory_method.example;
import design_pattern.factory_method.example.factory.Dialog;
import design_pattern.factory_method.example.factory.HtmlDialog;
import design_pattern.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: 執行結果 (Html-Dialog)
```
<button>Test Button</button>
Click! Button says - 'Hello World!'
```

*****
PHP版:
```
<?php
/**
* Factory Method Design Pattern
*
* Intent: Provides an interface for creating objects in a superclass, but
* allows subclasses to alter the type of objects that will be created.
*
* Example: In this example, the Factory Method pattern provides an interface
* for creating social network connectors, which can be used to log in to the
* network, create posts and potentially perform other activities—and all of
* this without coupling the client code to specific classes of the particular
* social network.
*/
/**
* The Creator declares a factory method that can be used as a substitution for
* the direct constructor calls of products, for instance:
*
* - Before: $p = new FacebookConnector();
* - After: $p = $this->getSocialNetwork;
*
* This allows changing the type of the product being created by
* SocialNetworkPoster's subclasses.
*/
abstract class SocialNetworkPoster
{
/**
* The actual factory method. Note that it returns the abstract connector.
* This lets subclasses return any concrete connectors without breaking the
* superclass' contract.
*/
abstract public function getSocialNetwork(): SocialNetworkConnector;
/**
* When the factory method is used inside the Creator's business logic, the
* subclasses may alter the logic indirectly by returning different types of
* the connector from the factory method.
*/
public function post($content): void
{
// Call the factory method to create a Product object...
$network = $this->getSocialNetwork();
// ...then use it as you will.
$network->logIn();
$network->createPost($content);
$network->logout();
}
}
/**
* This Concrete Creator supports Facebook. Remember that this class also
* inherits the 'post' method from the parent class. Concrete Creators are the
* classes that the Client actually uses.
*/
class FacebookPoster extends SocialNetworkPoster
{
private $login, $password;
public function __construct(string $login, string $password)
{
$this->login = $login;
$this->password = $password;
}
public function getSocialNetwork(): SocialNetworkConnector
{
return new FacebookConnector($this->login, $this->password);
}
}
/**
* This Concrete Creator supports LinkedIn.
*/
class LinkedInPoster extends SocialNetworkPoster
{
private $email, $password;
public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}
public function getSocialNetwork(): SocialNetworkConnector
{
return new LinkedInConnector($this->email, $this->password);
}
}
/**
* The Product interface declares behaviors of various types of products.
*/
interface SocialNetworkConnector
{
public function logIn(): void;
public function logOut(): void;
public function createPost($content): void;
}
/**
* This Concrete Product implements the Facebook API.
*/
class FacebookConnector implements SocialNetworkConnector
{
private $login, $password;
public function __construct(string $login, string $password)
{
$this->login = $login;
$this->password = $password;
}
public function logIn(): void
{
echo "Send HTTP API request to log in user $this->login with " .
"password $this->password\n";
}
public function logOut(): void
{
echo "Send HTTP API request to log out user $this->login\n";
}
public function createPost($content): void
{
echo "Send HTTP API requests to create a post in Facebook timeline.\n";
}
}
/**
* This Concrete Product implements the LinkedIn API.
*/
class LinkedInConnector implements SocialNetworkConnector
{
private $email, $password;
public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}
public function logIn(): void
{
echo "Send HTTP API request to log in user $this->email with " .
"password $this->password\n";
}
public function logOut(): void
{
echo "Send HTTP API request to log out user $this->email\n";
}
public function createPost($content): void
{
echo "Send HTTP API requests to create a post in LinkedIn timeline.\n";
}
}
/**
* The client code can work with any subclass of SocialNetworkPoster since it
* doesn't depend on concrete classes.
*/
function clientCode(SocialNetworkPoster $creator)
{
// ...
$creator->post("Hello world!");
$creator->post("I had a large hamburger this morning!");
// ...
}
/**
* During the initialization phase, the app can decide which social network it
* wants to work with, create an object of the proper subclass, and pass it to
* the client code.
*/
echo "Testing ConcreteCreator1:\n";
clientCode(new FacebookPoster("john_smith", "******"));
echo "\n\n";
echo "Testing ConcreteCreator2:\n";
clientCode(new LinkedInPoster("john_smith@example.com", "******"));
```
- 前言:為什么要學數據結構和算法?
- 第一章:數據結構和算法
- 什么是數據結構?
- 什么是算法?
- 1.從接口開始
- 2.算法分析
- 3.ArrayList
- 4.LinkedList
- 5.雙鏈表
- 6.樹的遍歷
- 7.到達的哲學
- 8.索引器
- 9.Map接口
- 10.哈希
- 11.HashMap
- 12.TreeMap-二叉樹
- 13.二叉搜索樹
- 14.數據持久化
- 15.排序
- 第二章:經典算法解析
- 1.兩數之和
- 2.兩數相加
- 3.無重復字符的最長子字符串
- 4.兩個排序數組的中值
- 5.最長回文子串
- 6.鋸齒形變換
- 7.反轉整數
- 8.合并K個排序列表
- 9.鏈表循環
- 10.除Self之外的數組乘積
- 11.4的威力
- 12.蛙跳
- 13.將交叉口大小設置為至少兩個
- 14.最大的塊,使其分類
- 15.到達點
- 16.階乘零點函數的前像大小
- 17.建造一個大的島嶼
- 18.唯一字母串
- 19.樹的距離之和
- 20.猜詞游戲
- 21.節點的最短路徑
- 22.矩形區域II
- 23.K-相似字符串
- 24.雇傭K工人的最低成本
- 25.至少為K的最短子陣
- 26.獲取所有key的最短路徑
- 27.加油站的最小數量
- 28.有利可圖的計劃
- 29.細分圖中的可達節點
- 30.超級蛋掉落
- 31.最大頻率疊加
- 32.有序隊列
- 33.最多N個給定數字集的數字
- 34.DI序列的有效置換
- 35.貓和老鼠
- 第三章:高級算法解析
- 找出數組中重復的數字
- 不修改數組找出重復的數字
- 二維數組中的查找
- 替換空格
- 從尾到頭打印鏈表
- 重建二叉樹
- 二叉樹的下一個結點
- 用兩個棧實現隊列
- 用兩個隊列實現棧用兩個隊列實現棧
- 斐波那契數列
- 跳臺階
- 變態跳臺階
- 矩形覆蓋
- 旋轉數組的最小數字
- 矩陣中的路徑
- 機器人的移動范圍
- 剪繩子
- 二進制中 1 的個數
- 數值的整數次方
- 打印從 1 到最大的 n 位數
- 在O(1)時間內刪除鏈表節點
- 刪除鏈表中重復的節點
- 正則表達式匹配
- 表示數值的字符串
- 調整數組順序使奇數位于偶數前面
- 鏈表中倒數第k個結點
- 鏈表中環的入口結點
- 反轉鏈表
- 合并兩個排序的鏈表
- 樹的子結構
- 二叉樹的鏡像
- 對稱的二叉樹
- 順時針打印矩陣
- 包含min函數的棧
- 棧的壓入、彈出序列
- 不分行從上到下打印二叉樹
- 把二叉樹打印成多行
- 按之字形打印二叉樹
- 二叉搜索樹的后序遍歷序列
- 二叉樹中和為某一值的路徑
- 復雜鏈表的復制
- 二叉搜索樹與雙向鏈表
- 序列化二叉樹
- 字符串的排列
- 數組中出現次數超過一半的數字
- 獲取數組中最小的k個數
- 數據流中的中位數
- 連續子數組的最大和
- 整數中1出現的次數
- 數字序列中某一位的數字
- 把數組排成最小的數
- 把數字翻譯成字符串
- 禮物的最大價值
- 最長不含重復字符的子字符串
- 丑數
- 第一個只出現一次的字符
- 字符流中第一個不重復的字符
- 兩個鏈表的第一個公共結點
- 數字在排序數組中出現的次數
- 0到n-1中缺失的數字
- 數組中數值和下標相等的元素
- 二叉搜索樹的第k個結點
- 二叉樹的深度
- 平衡二叉樹
- 數組中只出現一次的兩個數字
- 數組中唯一只出現一次的數字
- 和為S的兩個數字
- 和為S的連續正數序列
- 翻轉單詞順序
- 左旋轉字符串
- 滑動窗口的最大值
- 撲克牌的順子
- 第四章:設計模式
- 設計模式概述
- 創建型模式
- 工廠方法
- 抽象工廠
- 生成器
- 原型
- 單例
- 結構型模式
- 適配器
- 橋接
- 組合
- 裝飾器
- 外觀
- 享元
- 代理
- 行為模式
- 責任鏈
- 命令
- 迭代器
- 中介者
- 備忘錄
- 觀察者
- 狀態
- 策略
- 模板方法
- 訪問者
- 第五章:服務器運維
- 1.從vim編輯器開始
- 2.文本瀏覽器
- 3.Bash:Shell、.profile、.bashrc、.bash_history
- 4.Bash:處理文件,pwd,ls,cp,mv,rm,touch
- 5.Bash:環境變量,env,set,export
- 6.Bash:語言設置,LANG,locale,dpkg-reconfigure locales
- 7.Bash:重定向,stdin,stdout,stderr,tee,pv
- 8.更多的重定向和過濾:head,tail,awk,grep,sed
- 9.Bash:任務控制,jobs,fg
- 10.Bash:程序退出代碼(返回狀態)
- 11:總結
- 12.文檔:man,info
- 13.文檔:Google
- 14.包管理:Debian 包管理工具aptitude
- 15.系統啟動:運行級別,/etc/init.d,rcconf,update-rc.d
- 16.處理進程,ps,kill
- 17.任務調度:cron,at
- 18.日志:/var/log,rsyslog,logger
- 19.文件系統:掛載,mount,/etc/fstab
- 20.文件系統:修改和創建文件系統,tune2fs,mkfs
- 21.文件系統:修改根目錄,chroot
- 22.文件系統:移動數據,tar,dd
- 23.文件系統:權限,chown,chmod,umask
- 24.接口配置,ifconfig,netstat,iproute2,ss,route
- 25.網絡:配置文件,/etc/network/interfaces
- 26.網絡:封包過濾配置,iptables
- 27.安全 Shell,ssh,sshd,scp
- 28.性能:獲取性能情況,uptime,free,top
- 29.內核:內核消息,dmesg
- 最后:打磨、洗練、重復:總復習
- 最終章:深入學習
- 算法思維導圖
- 學習目標
- 學習路線
- 學習要點
- 學習大綱
- 資源推薦