《大設》第十一篇之工廠方法模式

来源:http://www.cnblogs.com/liaidai/archive/2016/10/26/6000963.html
-Advertisement-
Play Games

前言 首先是例行的國際慣例,本文寫於本人學習設計模式的路上,適合同樣學習設計模式的朋友交流使用,大神誤入請留下您寶貴的意見,先行謝過; 前面我們已經學習過 簡單工廠模式 ,今天來學習第二個工廠模式 方法工廠模式 。 定義 百度百科 工廠方法模式的意義是定義一個創建產品對象的工廠介面,將實際創建工作推 ...


前言

  首先是例行的國際慣例,本文寫於本人學習設計模式的路上,適合同樣學習設計模式的朋友交流使用,大神誤入請留下您寶貴的意見,先行謝過;

  前面我們已經學習過簡單工廠模式,今天來學習第二個工廠模式方法工廠模式

定義

百度百科

工廠方法模式的意義是定義一個創建產品對象的工廠介面,將實際創建工作推遲到子類當中,核心工作類不再負責對象的創建,這樣核心類成為一個抽象工廠角色,僅負責具體工廠子類必須實現的介面,這樣進一步抽象化的好處是使得工廠方法模式可以使系統在不修改具體工廠角色的情況下引入新的產品。

個人拙見

前面我們已經學習了簡單工廠模式或者說是靜態工廠模式,在簡單工廠模式中,每添加一個產品子類的同時還需要去修改核心的工廠類,添加對應的判斷語句,這是對擴展開放的同時也對修改開放,違反了設計原則中的開閉原則,但是今天的工廠方法原則則正好彌補了這一缺陷,將核心工廠抽象為一個抽象介面,核心工廠不再負責具體產品的創建,僅僅負責具體工廠子類必須實現的介面,此外給每一類產品都設置一個工廠類,通過工廠類生成對應的產品,以此來達到遵循開閉原則的目的,其次,產品和工廠之間使用抽象的方式進行關聯,同樣遵循了依賴倒置原則,另外,單一職責原則自不必說,工廠方法還很好的遵循了迪米特法則,也就是最少知道法則。

當然,方法工廠模式肯定是針對複雜對象的創建時所使用的,如果一個類僅僅通過new就可以創建,當然就沒必要使用工廠方法模式了,不然就會增加不必要的工廠類,因為系統臃腫不利於後期維護。

類圖

上圖中,左半部分是產品介面以及具體的產品類,右半部分是工廠介面以及對應的工廠類,每一個工廠對應一種產品,這種方式就避過了簡單工廠模式中的判斷語句,如果要添加新的產品,那麼只需要讓產品類實現產品介面,對應的工廠類實現核心工廠介面即可,不必去修改原來的代碼,滿足開閉原則。

代碼實現

下麵就用JAVA代碼簡單的實現以下上述的類圖,首先需要一個產品介面:

public interface IProduct {
    public void productMethod();
}

然後是兩種具體的產品:

public class ProductA implements IProduct{
    @Override
    public void productMethod() {
        System.out.println("產品A方法");
    }
}

public class ProductB implements IProduct{
    @Override
    public void productMethod() {
        System.out.println("產品B方法");
    }
}

然後是抽象工廠介面:

public interface IFactory {
    public  IProduct  createProduct();
}

產品A對應的工廠:

public class ProductFactoryA implements IFactory{
    @Override
    public IProduct createProduct() {
        return new ProductA();
    }
}

產品B對應的工廠:

public class ProductFacctoryB implements IFactory{
    @Override
    public IProduct createProduct() {
        return new ProductB();
    }
}

測試類:

public class Test {
    public static void main(String[] args) {
        IFactory factory = new ProductFactoryA();
        IProduct productA = factory.createProduct();
        productA.productMethod();//產品A方法
        
        factory = new ProductFacctoryB();
        IProduct productB = factory.createProduct();
        productB.productMethod();//產品B方法
    }
}

通過上述的測試代碼,我們可以看到,在客戶端我們可以隨意的切換具體的產品工廠和具體產品,並且不需要修改任何代碼,如果需要添加新的產品,也只是需要添加具體的產品類和對應的工廠類即可,然後就可以直接在客戶端調用,並不需要修改任何原來的代碼,這也正好滿足了對擴展開放對修改關閉的開閉原則,同時也去除掉了簡單工廠中的ifelse的多重判斷語句。

使用場景

該部分內容轉自博客園左瀟龍博客

應用場景一:JDBC連接

使用JDBC連接資料庫時,如果資料庫是mysql,那麼就需要使用mysql的驅動,如果是Oracle資料庫,那麼就需要使用Oracle的驅動,但是我們在寫JDBC程式的時候大部分是一樣的,不一樣的只是一樣那就是驅動包不同(前提是SQL是標準的SQL),雖然我們提供給JDBC程式的驅動包不同,但是返回的Connection卻是相同的,這是怎麼做到的呢?其實這裡使用的就是工廠方法模式,具體的流程如下:

首先,JDBC API提供了整個資料庫操作的框架,具體的資料庫的操作細節由各個資料庫提供商自己實現,而我們要操作的僅僅是JDBC API,而不是具體的資料庫細節,這就用到了java.sql包中的Driver和Connection介面,這兩個介面的部分代碼如下所示:

package java.sql;

import java.sql.DriverPropertyInfo;
import java.sql.SQLException;

/**
 * The interface that every driver class must implement.
 */
public interface Driver {

    Connection connect(String url, java.util.Properties info)
        throws SQLException;

    boolean acceptsURL(String url) throws SQLException;
}

package java.sql;

import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * <P>A connection (session) with a specific
 * database. SQL statements are executed and results are returned
 * within the context of a connection.
 * <P>
 */
public interface Connection  extends Wrapper {

    Statement createStatement() throws SQLException;

    PreparedStatement prepareStatement(String sql) throws SQLException;

}

在Driver介面中,註釋中給出了所有的驅動類都必須實現這個介面,也就是說所有的驅動類都必須實現Driver介面,否則沒法使用,然後在Driver介面中還有一個connect()方法,返回一個Connnection用以操作資料庫,但是Connection也是一個介面,只給出了約束,因此各個資料庫提供商在提供驅動類的同時還需要給出Connection的實現類,用來具體的操作資料庫的crud等操作,因此不同的資料庫就需要提供不同的驅動類和連接實現類,如mysql要提供com.jdbc.mysql.Driver以及mysql的ConnectionImpl,Oracle要提供oracle.jdbc.driver.OracleDriver以及Oracle的ConnectionImpl類。

說到這裡有人就奇怪了,說我平時寫JDBC的時候也沒看到Driver這個介面啊,我就用到了DriverManager類,這是因為在載入資料庫驅動的時候,在Driver類中自動的將驅動類註冊給了DriverManager,這時候就可以使用DriverManager來獲取到資料庫連接了,因此可能會看不到Driver介面的存在,但是它確確實實是存在的,下麵可以看一下mysql的Driverr類的代碼:

public class Driver extends NonRegisteringDriver
  implements java.sql.Driver
{
  public Driver()
    throws SQLException
  {
  }

  static
  {
    try
    {
      DriverManager.registerDriver(new Driver());//註冊驅動
    } catch (SQLException E) {
      throw new RuntimeException("Can't register driver!");
    }
  }
}

上述幾個類的類圖如下(請原諒我又盜用了左大神的類圖):

在使用JDBC連接的時候,我們不需要知道mysql或者oracle是怎麼樣提供的資料庫操作的,我們只需要根據JDBC API中的Connection和DriverManager拿到資料庫連接,處理具體的資料庫業務即可,這個場景也是工廠方法模式的一個優點:屏蔽具體的產品類,也就是說具體的產品類如何變化調用者並不關心,客戶端只需要關心產品的介面,只要產品的介面不發生變化,那麼不管下層模塊如何變化都不會影響上層模塊的調用,這是遵循依賴倒置原則的具體表現。

應用場景二:

使用工廠方法模式並不是說只能使用面向介面,有時候也需要關心具體的產品實現類,因為每一類產品可能都有不同的地方,例如HashSet和ArrayList,這兩個類的具體實現也使用到了工廠方法模式,具體的類圖如下(我保證這是最後一次盜圖了,以後再也不敢了):

這時候一看,我擦,上面不是說要面向介面編程嘛,那我以後就使用Iterable和Iterator這倆了,這個,如果可以的話,類庫的設計者何必要設計什麼ArrayList、TreeSet這麼多集合類呢,這裡的每個具體的產品類都有其特性,因此這裡就不能單單的使用介面而是要使用其對應的產品類。

上述的兩個場景一個是面向介面的,一個是面向具體的產品的,這也是工廠方法模式提供的兩種使用情況:如果各個產品提供的功能類似,如mysql或者Oracle都是提供資料庫連接,那麼就可以使用面向介面的方式來處理,但是如果是針對於具體的產品,此時調用者清楚的知道應該使用哪個具體工廠服務,實例化該具體工廠,生產出具體的產品來,這就需要考慮倒具體的產品了,不能單單的是面向介面了

優點

  1. 代碼結構清晰,良好的封裝性。將產品的創建過程封裝在對應的工廠中,調用者不需要知道對象的創建過程,減少了模塊之間的耦合性;
  2. 良好的擴展性。在需要添加新的產品進來時,只需要添加符合要求的工廠類和產品類即可,不需要修改原有代碼,遵循開閉原則;
  3. 屏蔽具體的產品。如JDBCl連接,我們只需要根據API調用Connection和DriverManeger即可,不需要知道mysql或者oracle是如何實現底層的資料庫操作的;
  4. 良好的解耦合能力。上層模塊只需要知道抽象工廠即可,不需要知道具體的實現類,符合迪米特法則。

與簡單工廠模式的區別聯繫

首先,工廠方法模式是簡單工廠模式的擴展,給每一個產品都添加一個對應的工廠,使用抽象工廠作為核心工廠,核心工廠不再關註具體產品的創建而是交由它的子類來創建,但是在簡單工廠模式中僅僅只存在一個工廠,並且是核心工廠;

其次,工廠方法模式中避免了在核心工廠中使用多重判斷語句的詬病,並且滿足對於修改關閉對於擴展開放的開閉原則;

第三,如果一個模塊中僅僅存在一個工廠就可以滿足條件,那麼完全沒有必要為每個產品都產生對應的工廠,雖然簡單工廠模式不滿足開閉原則,但是不能否認簡單工廠模式也是很強大的;

總結

工廠方法模式是一個創建型的設計模式,遵循了單一職責、依賴倒置以及開閉原則,有兩種使用場景,一種是客戶端不需要知道具體的產品是如何實現的,只需要面向介面編程即可,另一種是客戶端完全清楚每種的產品的實現和用途,客戶端需要使用具體的產品了。

參考文章

  1. http://www.cnblogs.com/zuoxiaolong/p/pattern5.html
  2. http://www.cnblogs.com/cbf4life/archive/2009/12/20/1628494.html
  3. http://blog.csdn.net/zhengzhb/article/details/7348707

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • macOS Sierra 已經幫我們預裝了 Ruby、PHP(5.6)、Perl、Python 等常用的腳本語言,以及 Apache HTTP 伺服器。由於 nginx 既能作為 HTTP 伺服器也能作為反向代理伺服器,且配置簡單,這裡我們用 nginx 代替 Apache 作為我們預設的 HTTP ...
  • 最近自學了一下NodeJS,然後做了一個小demo,實現歌曲的添加、修改、播放和刪除的功能,其中自然要實現音樂和圖片的上傳功能。於是上網查找資料,找到了一個formidable插件,該插件可以很好的實現文件的上傳功能。該小demo用到了MySQL資料庫,所有的數據都存放到了資料庫中。下麵簡單說一些如... ...
  • 可以在Index.php文件下 <?php 之前添加<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> ...
  • REST是一種架構風格,其核心是面向資源,REST專門針對網路應用設計和開發方式,以降低開發的複雜性,提高系統的可伸縮性。REST提出設計概念和準則為: REST是一種架構風格,其核心是面向資源,REST專門針對網路應用設計和開發方式,以降低開發的複雜性,提高系統的可伸縮性。REST提出設計概念和準 ...
  • 監聽器的原理: 被監聽對象→對象擁有的事件→捕獲到事件變化→監聽器捕捉事件→監聽器處理該事件 Web伺服器上有4個範圍,拋開page範圍,還有request範圍,session範圍,application範圍。這些範圍的對象什麼時候創建,什麼時候銷毀,什麼時候往範圍中存放了數據,什麼時候替換了存放的 ...
  • 位元組流與和字元流的使用非常相似,兩者除了操作代碼上的不同之外,是否還有其他的不同呢?實際上位元組流在操作時本身不會用到緩衝區(記憶體),是文件本身直接操作的,而字元流在操作時使用了緩衝區,通過緩衝區再操作文件,如圖12-6所示。下麵以兩個寫文件的操作為主進行比較,但是在操作時位元組流和字元流的操作完成之後 ...
  • 最近在苦於思考kmeans演算法的MPI並行化,花了兩天的時間先把串列版本實現了。 聚類問題就是給定一個元素集合V,其中每個元素具有d個可觀察屬性,使用某種演算法將V劃分成k個子集,要求每個子集內部的元素之間相異度儘可能低,而不同子集的元素相異度儘可能高。 下麵是google到該演算法的一個流程圖,表意清 ...
  • 通過打電話的例子講解TCP的三次握手和四次揮手,通俗易懂,容易理解 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...