設計模式之工廠模式(爲何不少人以爲工廠模式沒有用)

前言

隨着編程技術的不斷髮展,面嚮對象語言和麪向對象程序設計逐漸成爲主流。這就不可避免地涉及到了對象的建立。建立一個對象,並使用已經定義好的方法,看起來也很清晰和簡單。有的時候,在不一樣的狀況下須要不一樣子類的對象,如何下降耦合度、方便地進行切換,而不須要將全部實例化該對象的地方都進行修改,則涉及到了模式。java

下面將依次介紹簡單工廠模式、工廠方法模式、抽象工廠模式,說明他們是如何實現建立對象這一功能的。(後文將三者統稱爲工廠模式。)編程

簡單工廠模式

一個很簡單的作法,既然你說可能要使用不一樣的對象,那我將全部建立對象的操做都集中起來,只在一個地方進行對象的建立,其餘地方都經過這裏來建立對象。之後即使要修改,也只須要改一處地方不就能夠了。設計模式

以生產鼠標爲例,咱們須要有線鼠標和無線鼠標。則大體的代碼以下:less

abstract class Mouse {
}

class WiredMouse : Mouse {
}

class WirelessMouse : Mouse {
}

class SimpleFactory_1 {
	public Mouse createMouse(String type) {
    if ("wired".equals(type)) {
    	return new WiredMouse();  
    } else if ("wireless".equals(type)) {
      return new WirelessMouse();
    } else {
      // unknown type !
      return null;
    }
  }
}
複製代碼

簡單工廠模式十分簡單直觀,代碼量也不多。這份代碼的一個改進之處是將 createMouse 的參數從 String 類型改爲枚舉類型,以避免使用時拼寫錯誤,也能更好地對參數進行限制。spa

簡單工廠模式的缺點是,若是咱們須要增長新的鼠標,那麼咱們就必須修改 createMouse 方法,增長 if/else 語句。這樣就破壞了"對修改封閉"的原則。設計

簡單工廠模式能夠定義成靜態方法,也能夠定義成實例方法。他們的特色以下:code

  • 靜態方法
    • 無需建立對象就能使用
    • 不能使用繼承來改變建立方法的行爲
  • 實例方法
    • 須要建立對象才能使用
    • 可使用繼承來改變建立方法的行爲

更進一步,假設有 A、B 兩家公司都生產有線和無線鼠標,他們的鼠標定義以下:對象

// A 的鼠標
class AWiredMouse : WiredMouse {
}

class AWirelessMouse : WirelessMouse {
}

// B 的鼠標
class BWiredMouse : WiredMouse {
}

class BWirelessMouse : WirelessMouse {
}
複製代碼

對應的簡單工廠模式代碼以下:繼承

class SimpleFactory_2 {
	public Mouse createMouse(String factory, String type) {
    if ("A".equeals(factory)) {
      if ("wired".equals(type)) {
        return new AWiredMouse();  
      } else if ("wireless".equals(type)) {
        return new AWirelessMouse();
      } else {
        // unknown type !
        return null;
      }  
    } else if ("B".equals(factory)) {
      if ("wired".equals(type)) {
        return new BWiredMouse();  
      } else if ("wireless".equals(type)) {
        return new BWirelessMouse();
      } else {
        // unknown type !
        return null;
      }
    } else {
      // unknown factory !
      return null;
    }
  }
}
複製代碼

能夠看到代碼開始複雜起來了,若是還有更多公司 C、D 也在生產鼠標,或者有更多的鼠標類型,那麼這個工廠類將會更將複雜。開發

工廠方法模式

下面咱們就來看看工廠方法模式是如何處理這種問題。

abstract class Factory {
	public Mouse CreateMouse(String type);
}

class AFactory_2 : Factory {
  public Mouse CreateMouse(String type) {
    if ("wired".equals(type)) {
      return new AWiredMouse();  
    } else if ("wireless".equals(type)) {
      return new AWirelessMouse();
    } else {
      // unknown type !
      return null;
    } 
  }
}

class BFactory_2 : Factory {
  public Mouse CreateMouse(String type) {
    if ("wired".equals(type)) {
      return new BWiredMouse();  
    } else if ("wireless".equals(type)) {
      return new BWirelessMouse();
    } else {
      // unknown type !
      return null;
    } 
  }
}
複製代碼

能夠看到,工廠方法模式中,定義了多個工廠類分別對應各自的公司,每一個類只負責建立本身的鼠標,而不用管其餘公司的。

若是還有更多的公司,則定義新的工廠類便可,不須要修改已有的代碼。

若是須要生產更多類型的鼠標,則須要對工廠都作修改,增長相應的 if/else 語句來處理。這一點與簡單工廠模式相似。

工廠方法模式,體現了"對擴展開發,對修改封閉"。

這裏爲了說明使用工廠方法模式相比簡單工廠模式的優勢,特地給鼠標增長了公司(A、B)和類型(有線、無線)這兩個緯度,以便說明工廠方法模式對代碼職責進行劃分。

實際上,對應簡單工廠 SimpleFactory_1 也能夠改爲工廠方法模式,代碼以下:

abstract class Factory {
	public Mouse CreateMouse();
}

class WiredFactory_1 : Factory {
  public Mouse CreateMouse() {
    return new AWiredMouse();  
  }
}

class WirelessFactory_1 : Factory {
  public Mouse CreateMouse() {
     return new AWirelessMouse();
  }
}
複製代碼

抽象工廠模式

這些公司除了生產鼠標外,還會生產鍵盤等產品。鍵盤的定義以下:

abstract class Keyboard {
}

class WiredKeyboard : Keyboard {
}

class WirelessKeyboard : Keyboard {
}

// A
class AWiredKeyboard : WiredKeyboard {
}

class AWirelessKeyboard : WirelessKeyboard {
}

// B
class BWiredKeyboard : WiredKeyboard {
}

class BWirelessKeyboard : WirelessKeyboard {
}
複製代碼

對應的抽象工廠模式以下:

abstract class Factory {
  public Mouse createMouse(String type);
  
  public Keyboard createKeyboard(String type);
}

class AFactory_2 : Factory {
  public Mouse createMouse(String type) {
    // 與以前一致
  }
  
  public Keyboard createKeyboard(String type) {
    if ("wired".equals(type)) {
      return new AWiredKeyboard();  
    } else if ("wireless".equals(type)) {
      return new AWirelessKeyboard();
    } else {
      // unknown type !
      return null;
    } 
  }
}

class BFactory_2 : Factory {
  public Mouse createMouse(String type) {
    // 與以前一致
  }
  
  public Keyboard createKeyboard(String type) {
    if ("wired".equals(type)) {
      return new BWiredKeyboard();  
    } else if ("wireless".equals(type)) {
      return new BWirelessKeyboard();
    } else {
      // unknown type !
      return null;
    } 
  }
}
複製代碼

能夠看到:如今一個工廠會負責生產鼠標、鍵盤等多種產品,而且每一個工廠只負責本身的部分。

若是還有新的公司也能夠生成鼠標、鍵盤這些產品,則只須要添加新的工廠類便可,無需修改已有的類。

若是還要生產更多的產品好比顯示器等,則須要修改全部的工廠類。

這裏的例子,工廠方法也能夠拆成抽象工廠來表示。。

總結

簡單工廠模式的重點在於將建立產品的代碼統一在一處,方便管理。

工廠方法模式、抽象工廠模式除了將建立產品的代碼統一在一處,還提供了一種便捷地更換產品系列的能力——當我須要 A 公司的產品,那麼就使用 AAFactory;當我須要 B 公司的產品,那麼就使用 BFactory;只須要修改使用的工廠便可實現將全部的產品都改爲對應的產品,大大下降了耦合度和修改的成本。

不少時候,咱們只須要用到簡單工廠模式就足夠了。但這並非工廠方法模式、抽象工廠模式沒有用,而是不少時候沒有這種需求——咱們只須要一種類型的產品就足夠了,並不須要同時支持多種產品;在這種狀況下,使用簡單工廠模式的確會更加簡單。

然而,一旦咱們同時須要不一樣類型的產品,那麼簡單工廠模式就很容易使得方法體快速膨脹,使用工廠方法模式和抽象工廠模式能夠更好地對代碼和職責進行劃分。

工廠方法模式只生產一種產品,而抽象工廠模式則是生產一系列產品(,一般這些產品之間有必定聯繫的)。抽象工廠模式生產其中的每一個產品時,一般使用工廠方法模式。

參考

  • 《Head First 設計模式》

  • 《設計模式 可複用面向對象軟件的基礎》

相關文章
相關標籤/搜索