設計模式——工廠模式

来源:http://www.cnblogs.com/andyong/archive/2016/03/20/5298983.html
-Advertisement-
Play Games

常說的工廠模式一般認為有三種:簡單工廠、工廠方法模式、抽象工廠模式。其中簡單工廠嚴格上來說不是一種設計模式,而被認為是一種好的編碼習慣/風格。 簡單工廠 簡單工廠的本質就是封裝變化的代碼,使客戶代碼將要面臨的改變變少。而且被封裝的代碼也有了更好的復用性,比如服務多個客戶端或者被繼承/包裝等工具來擴展


常說的工廠模式一般認為有三種:簡單工廠、工廠方法模式、抽象工廠模式。其中簡單工廠嚴格上來說不是一種設計模式,而被認為是一種好的編碼習慣/風格。

 

簡單工廠

簡單工廠的本質就是封裝變化的代碼,使客戶代碼將要面臨的改變變少。而且被封裝的代碼也有了更好的復用性,比如服務多個客戶端或者被繼承/包裝等工具來擴展。

下麵以腎5和腎6為對象來說明

//define product(iphone) interface
public interface IPhone{
    public void model();    
}

//iphone5
public class Phone5 implements IPhone{
    public void model(){
        System.out.printf("it is ipone5");
    }
}

//iphone6
public class Phone6 implements IPhone{
    public void model(){
        System.out.printf("it is ipone6");
    }
}


//Client: An apple store
public class AppleStore{
    public IPhone sellPhone(String model){
        if(model.equals("5")){
            return new Phone5();
        } else if(model.equals("6")){
            return new Phone6();
        } 
    }
}

//change it with SimpleFactory
//Client: An apple store
public class AppleStore{
    private PhoneFactory factory;
    public IPhone sellPhone(String model){
        return factory.createPhone(model);
    }
}

public class PhoneFactory{
    public IPhone createPhone(String model){
        if(model.equals("5")){
            return new Phone5();
        } else if(model.equals("6")){
            return new Phone6();
        } 
    }
}

      從上面使用簡單工廠的前後我們可以看出,簡單工廠僅僅就是把if else那部分易變的代碼,因為會不斷的有新產品退出或者有舊的產品不再出售,進行了封裝。但是這樣的改變帶來的優點也是很明顯的。比如前面提到的服務多個客戶端,假如還有一個倉庫管理系統也可以利用這個PhoneFactory來創建iphone,而不用重新寫一份if else的代碼;再比如,假設在美國賣的是64GB版本,在中國賣的都是128GB版本,那麼可以保持客戶端的代碼不變,只要修改一下我們提供的介面類PhoneFactory即可達到相同信號但是不同產品的目的。也可以給AppleStore添加setFactory方法來設置不同版本的Factory以達到動態設定的效果。

      從"對修改關閉"的原則上來看,簡單工廠也能使代碼更貼近這種原則。採用簡單工廠之後,需要修改的只是自己控制的工廠代碼,而對於客戶代碼則不需要修改,遵循了“對修改關閉”原則。

      也有把createPhone寫成靜態方法的,這樣的做法就可以不用創建工廠實例,但缺點就是不能通過繼承來改變工廠。

工廠模式

      工廠方法模式:定義一個創建對象的介面,但由子類決定要實例化的類是哪一個,把類的實例化推遲到工廠子類中。

      下麵我們看一個具體的實例,使用工廠方法模式來重寫上面的賣手機模型。

 1 //change it with FactoryMode
 2 //Client: An apple store
 3 public class AppleStore{
 4     private PhoneFactory factory;
 5 
 6     public AppleStore(PhoneFactory factoy){
 7         this.factory = factory;
 8     }
 9 
10     public IPhone sellPhone(String model){
11         return factory.createPhone(model);
12     }
13 }
14 
15 //factory interface
16 public abstract class PhoneFactory{
17     public final IPhone createPhone(String model){
18         return wrap(assemble(model));
19     }
20 
21     public final void wrap(IPhone phone){
22         System.out.printf("wrap this phone");
23         return phone;
24     }
25 
26     abstract IPhone assemble(String model);
27 }
28     
29 // fatory of USA
30 public class PhoneFactoryA extends PhoneFactory{
31     public IPhone assemble(String model){
32         if(model.equals("5")){
33             return new Phone5ForA();
34         } else if(model.equals("6")){
35             return new Phone6ForA();
36         } 
37     }
38 }
39 
40 //factory of CHN
41 public class PhoneFactoryC extends PhoneFactory{
42     public IPhone assemble(String model){
43         if(model.equals("5")){
44             return new Phone5ForC();
45         } else if(model.equals("6")){
46             return new Phone6ForC();
47         } 
48     }
49 }
50 
51 
52 //applestore in usa
53 AppleStore usaStore = new AppleStore(new PhoneFactoryA());
54 //applestore in CHN
55 AppleStore chnStore = new AppleStore(new PhoneFactoryC());

      上面的工廠方法模型中,我們首先創建了一個工廠介面,然後分別實現了其子類USA工廠和CHN工廠,以分別製造適應不同國家的版本。Phone5ForA等對應的是不同國家的不同版本的類,代碼中沒有寫出來。如果又要開一個HK店,那麼只要實現一個HKPhoneFactoy子類即可。

      上面比較重要的一點是:寫了兩個final方法:創建手機和包裝手機,然後從代碼可以看出來,手機組裝好後,必須經過wrap(包裝)才可以從store中售出。我們知道final方法不可以被重寫,也就是說,上面的流程:“組裝-包裝-出售”是不可以在子類中被更改的,這樣的話,就可以防止不良工廠賣出沒有包裝盒的手機,即就對子類的一些行為進行了控制,這也是工廠方法模式中很重要的一點。

      工廠方法最重要的特點的就是創建工廠介面,使得能夠針對介面編程,而不是針對實現編程,簡單工廠就是針對實現編程。使用針對介面編程可以帶來更大的彈性。工廠方法介面的子類與簡單工廠中的工廠十分相似,從功能上看也是一致的,都實現了封裝變化部分的效果。但是從大局上看,簡單工廠僅僅就是實現封裝變化部分的效果,而工廠方法模式則是搭建了一個很有彈性的框架。

 

抽象工廠模式

抽象工廠定義:提供一個介面,用於創建相關或依賴對象的家族,而不需要明確指定具體類。

繼續上面的例子。我們知道手機是由多個零件組裝起來的,比如CPU,屏幕等,對於上面的Phone5和Phone6,組裝他們的時候都需要CPU和屏幕,但是各自的具體零件型號又不相同,這時,就該我們的抽象工廠方法上場了。

//define product(iphone) interface
public interface IPhone{
    protected IComponentFactory componentFactory;
    public void model();    
}

//iphone5
public class Phone5 implements IPhone{

    public Phone5(){
        componentFactory = new ComponentFactory5();
    }

    public void model(){
        System.out.printf("it is ipone5");
    }
}

//iphone6
public class Phone6 implements IPhone{

    public Phone6(){
        componentFactory = new ComponentFactory6();
    }

    public void model(){
        System.out.printf("it is ipone6");
    }
}

//ComponentFactory interface
public interface IComponentFactory{
    protected CPU cpu;
    protected Screen screen;
}

//ComponentFactory5
public class ComponentFactory5 implements IComponentFactory{
    public ComponentFactory5(){
        cpu = CPUFactory.createCPU(5);
        screen = ScreenFactory.createScreen(5);
}

//ComponentFactory6
public class ComponentFactory6 implements IComponentFactory{
    public ComponentFactory6(){
        cpu = CPUFactory.createCPU(6);
        screen = ScreenFactory.createScreen(6);
}

//cpu
public interface CPU{
}

public class CPU5 implements CPU{
}

public class CPU6 implements CPU{
}

public class CPUFactory{
    public static CPU createCPU(int model){
        if(model == 5){
              return new CPU5();
        } else if (model == 6){
              return new CPU6();
        }
}

//screen
public interface Screen{
}

public class Screen5 implements Screen{
}

public class Screen6 implements Screen{
}

public class ScreenFactory{
    public static Screen createScreen(int model){
        if(model == 5){
              return new Screen5();
        } else if (model == 6){
              return new Screen6();
        }
}

      在上面的代碼中,組裝Phone5和Phone6都需要CPU和Screen零件,於是他們都有一個零件工廠IComponentFactory(抽象工廠介面),各自的零件工廠都指定了自己需要什麼樣子的零件。而零件工廠創建零件的時候,則都是分別調用的具體零件工廠CPUFactory和ScreenFactory來創建具體的零件,也即本抽象工廠的實現中也應用到了前面提到的工廠方法模型。從這裡也可以發現:抽象工廠他們不真正的製造具體的對象,他們僅僅指定我們需要什麼要的對象,就好比抽象工廠方法就是一張裝配清單(比如大部分手機廠乾的就是這個活),而工廠方法則是負責製造這個裝配清單上的具體零件(比如高通、聯發科等就是乾的製造CPU的活),這也許是抽象工廠之所以被叫做抽象的原因。更進一步的我們可以發現,抽象工廠其實可以理解為組裝工廠,它的模型就是通過組合不同的Component來實現的;而工廠方法模式則是通過繼承,把具體實現交給子類來實現的。

      在上面的抽象工廠代碼中,Phone類不需要關心CPU和Screen具體是怎麼創建的(這些具體的創建都是CPUFactory和ScreenFactory),而只知道我需要哪種CPU和Screen,這樣就把Phone從具體的CPU和Screen類解耦了。

 


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

-Advertisement-
Play Games
更多相關文章
  • (一)變數的命名及賦值: var=value (1)、其中var是變數名,value是變數的值。如果value不包含任何空白字元(空格),就不需要包含單引號或雙引號 (2)、var=value不同於var (空格)=(空格)value,前者是賦值操作,後者是相等操作 (3)、在變數名前加$或者$(v
  • 本實驗主要是實現蜂鳴器的操作,蜂鳴器的操作是非常簡單的,只有把簡單的事情做好,方可談其他複雜的事。本實驗部分會利用verilog一些巨集定義語句,其實在VGA實驗部分已經出現過,這裡為了鞏固,再次調用相關巨集定義命令,已達到最大化的可移植性,請讀者務必掌握這種用法,很實用。 談及蜂鳴器或者LED,多多少
  • 這幾天呢,公司風波再起,去年一年公司CTO換啦4任,CEO換啦三個,這不剛來個新老大,感覺還不錯,卻沒乾過3個月又要走,索性趁老大們走來走去的時候,就給自己空出來,稍稍總結一下剛寫的一個日誌服務組件中用到的RabbitMQ,在.net中的實戰中應用。 首先不去討論我的日誌組件怎麼樣。因為有些日誌需要
  • 少爺菜鳥一枚,求輕噴。 今天在網上無意間看到一道面試題,感覺挺有意思的,題目是這樣:"請用任意一種面向對象語言實現一個電腦控制台程式,要求輸入兩個數和運算符號,輸出結果”。 第一遍的時候還在想,居然還有這麼簡單的面試題?花了幾分鐘敲了一下這個程式,代碼如下(為了方便,代碼本身不規範,很多情況並未考
  • oauth2.0 sso jackical
  • 在平時寫程式的時候往往會遇到這樣的需求,對於某些資源我們只想讓其只能由一個對象進行訪問,從而保證其完整性。比如,配置文件,工具類,線程池,緩存,日誌對象等。對這些資源進行訪問的對象我們只需要一個,當能對其進行讀寫的對象多了的時候就可能由於邏輯上的問題導致了很多意想不到的結果。在這個的背景下,結合了面...
  • 註冊組件以後,通過容器或 ILifetimeScope 的 Resolve 方法解析服務: 請註意,本例使用 ILifetimeScope 解析服務,而不是直接從容器解析。儘管可以從根容器直接解析組件,但是,某些情況下導致記憶體泄露。建議始終從 ILifetimeScope對象解析組件,以確保服務的實
  • 上一篇開源任務管理平臺TaskManager介紹發佈後,有網友聯繫我看看能不能做個後臺管理界面,方便管理系統中所有的任務。由於時間和技術問題1.0版本的時候,新增了一個3分鐘讀取配置文件動態修改任務的功能,不過總體來說還是不直觀,操作不便捷。剛好最近接觸到了Owin,可以實現自己的宿主程式運行Web
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...