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

来源: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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...