在中國歷史上,房子經常與當下同樣稀缺,住房問題一樣是一個讓百姓苦惱的社會熱點。git
在拆違章建築還不盛行的年代,咱們能夠選擇在深山老林裏本身修建住所。在 Java 中多是這樣實現的:github
class BuildingA {
private String name;
public BuildingA(String name) {
this.name = name;
}
public void build() {
System.out.println(name + " is building");
}
}
class BuildingB {
private String name;
public BuildingB(String name) {
this.name = name;
}
public void build() {
System.out.println(name + " is building");
}
}
// 使用
BuildingA buildingA = new BuildingA("bedroom");
BuildingB buildingB = new BuildingB("kitchen");
buildingA.build();
buildingB.build();複製代碼
村裏的牛大哥在建完兩間房子以後,後知後覺:本身想要的房間格局不一樣,可是風格得相同,能夠把公共的部分抽離出來:設計模式
interface IBuilding {
void build();
}
abstract class AbstractBuilding implements IBuilding {
protected void buildCommon(){
System.out.println("Europe style"); // 公共的部分
}
}
class BuildingAs extends AbstractBuilding {
private String name;
public BuildingAs(String name){
this.name = name;
}
@Override
public void build() {
this.buildCommon();
System.out.println(name + " is building");
}
}
class BuildingBs extends AbstractBuilding {
private String name;
public BuildingBs(String name) {
this.name = name;
}
@Override
public void build() {
this.buildCommon();
System.out.println(name + " is building");
}
}
// 使用
BuildingAs buildingA = new BuildingAs("bedroom");
BuildingBs buildingB = new BuildingBs("kitchen");
buildingA.build();
buildingB.build();複製代碼
可是這麼作以後,牛大哥發如今建造的時候並無省力,他向村口的王師傅請教,爲何我考慮了不少反而沒什麼做用呢?app
王師傅告訴他:雖然你找出了一些公共的流程,但在實際建造過程當中,你仍是完整的過了全部的流程(構造方法不一樣,每次都要 new
對象)。另外,ide
另外,你對房屋的需求並很少,因此優點不夠明顯。函數
說着掏出一個寶盒,盒子裏有不少設計圖:下次你能夠委託我來造一些組件(再也不須要本身 new
):測試
public class SimpleFactory {
public static IBuilding getProduct(String name){
if("bedroom".equals(name)){
return new BuildingA(name);
}else if("kitchen".equals(name)){
return new BuildingB(name);
}else{
throw new IllegalArgumentException();
}
}
}
// 使用
IBuilding buildingA = SimpleFactory.getProduct("bedroom");
IBuilding buildingB = SimpleFactory.getProduct("kitchen");
buildingA.build();
buildingB.build();複製代碼
王師傅幫助下的牛大哥在後面的建造中感受輕鬆多了。ui
這就是「簡單工廠模式」,也稱做「靜態工廠方法模式」。this
它有如下幾個優勢:spa
而牛二哥明顯沒有那麼幸運,他的妻子追求個性,而且很善變,老是在建造過程當中更改需求。
雖然牛二哥也去王師傅那獲取組件,每次王師傅都要拿出他的寶盒,在裏面翻一遍,再告訴牛二哥 —— 這個我不會造。站在 OCP(開放封閉原則)的角度講,該模式的擴展不夠良好,每次有新的模型後都要修改工廠。
老王師傅也經不起折騰,想着不能閉關鎖國,就把本身會建造的組件貼在顯眼的地方,有新的組件直接加在上面就好:
interface IFactory {
public IBuilding createBuilding();
}
class FactoryA implements IFactory{
@Override
public IBuilding createBuilding() {
// 能夠進行復雜的處理,每一種方法對應一種模型
return new BuildingA("bedroom");
}
}
class FactoryB implements IFactory{
@Override
public IBuilding createBuilding() {
return new BuildingA("kitchen");
}
}
class FactoryC implements IFactory{
@Override
public IBuilding createBuilding() {
return new BuildingA("restroom");
}
}
// 使用
FactoryA factoryA = new FactoryA();
FactoryB factoryB = new FactoryB();
FactoryC factoryC = new FactoryC();
factoryA.createBuilding();
factoryB.createBuilding();
factoryC.createBuilding();複製代碼
這樣你們的溝通是方便了不少,並且老王也不用每次都搜一遍傳家寶盒。
這種模式被 GOF 稱做「工廠方法模式」。
工廠方法模式(Factory Method Pattern)是一種實現了「工廠」概念的面向對象設計模式。就像其餘建立型模式同樣,它也是 處理在不指定對象具體類型的狀況 下建立對象的問題。定義以下:
定義一個用於建立對象的接口,讓子類決定實例化哪個類。Factory Method 使一個類的實例化延遲到其子類。 — 《設計模式》 GOF
從以上也可看出:工廠作的事很簡單 —— 封裝內部的實現細節。
它能夠帶來如下好處:
咱們可能會趕上如下問題:
工廠方法模式針對的是一個產品等級結構,當要處理多個產品等級結構時(ex. 創建不一樣小區,小區裏有不一樣樓宇,樓裏還有不一樣戶型),咱們不但願對每一個模型都創建一個工廠,這太糟糕了,來看看「抽象工廠模式」是如何解決的。
爲建立一組相關或相互依賴的對象提供一個接口,並且無需指定他們的具體類。
咱們也可把「一組相關或相互依賴的對象」稱做「產品族」。
利用抽象工廠,咱們能夠這麼寫:
interface IBuildingA {
void buildA();
}
interface IBuildingB {
void buildB();
}
interface IFactory {
public IBuildingA createBuildingA();
public IBuildingB createBuildingB();
}
class BuildingA implements IBuildingA {
... // 省略構造函數
@Override
public void buildA() {
System.out.println((name + "is building"));
}
}
class BuildingB implements IBuildingB {
... // 省略構造函數
@Override
public void buildB() {
System.out.println(name + " is building");
}
}
class Factory implements IFactory{
@Override
public IBuildingA createBuildingA() {
return new BuildingA("big bedroom");
}
@Override
public IBuildingB createBuildingB() {
return new BuildingB("small bedroom");
}
}
// 測試
Factory factory = new Factory();
factory.createBuildingA();
factory.createBuildingB();複製代碼
咱們能夠直接在一個工廠類中實現多個方法,這樣不用管理多個工廠,使用和管理起來都更方便。
若是說工廠方法解決問題的方式是「廣搜」,那抽象工廠亦可看做「深搜」。
以上,咱們使用到了三種設計模式:簡單工廠(靜態工廠方法)、工廠方法、抽象工廠。
在三種模式中,咱們要作的都是將工廠的初始化與構造分離。
雖然比起直接 new
要增長很多代碼,但在後期維護的時候,能給咱們提供不少的便利。
看完 Java 版本,咱們再來看看 Scala 是如何實現的。
在 Scala 中,依舊能夠用相似 Java 的方式來實現,只用把 Java 中的關鍵字 interface
換成 trait
便可,直接看代碼吧。
trait IBuilding {
def show()
}
case class SimpleBuilding(name: String)extends IBuilding {
def show = println("SimpleBuilding " + name + " is building")
}
case class LuxuryBuilding(name: String) extends IBuilding{
def show = println("LuxuryBuilding " + name + " is building")
}
object ConstructionFactory {
def createBuilding(kind: String): IBuilding = kind match {
case "Simple" => SimpleBuilding("Simple")
case "Luxury" => LuxuryBuilding("Luxury")
}
}
object Test extends App {
val simpleBuilding: IBuilding = ConstructionFactory.createBuilding("Simple")
val luxuryBuilding: IBuilding = ConstructionFactory.createBuilding("Luxury")
simpleBuilding.show()
luxuryBuilding.show()
}複製代碼
除了這種方式,Scala 還爲咱們提供了一種相似構造器的語法 —— apply
,經過這種方式,咱們能夠省略工廠類,只需增長產品類接口的伴生對象:
object IBuilding {
def apply(kind: String): IBuilding = kind match {
case "Simple" => SimpleBuilding("Simple")
case "Luxury" => LuxuryBuilding("Luxury")
}
}複製代碼
調用者有更好的體驗:
val simpleBuilding: IBuilding = IBuilding("Simple")
val luxuryBuilding: IBuilding = IBuilding("Luxury")
simpleBuilding.show()
luxuryBuilding.show()複製代碼
嚴格意義講,這種方法並不屬於 GOF 提到的工廠方法,它缺乏了工廠模塊,咱們能夠稱之爲「靜態工廠模式」。
工廠方法與抽象工廠的實現與 Java 相似,代碼就不貼出來了。不瞭解 Scala 的同窗能夠參考源碼
以上,不難總結出工廠模式中的四種角色(簡單工廠模式中沒有抽象工廠):
IBuiiding
)。Buiiding
)Factory Method
),用於返回一個產品。抽象工廠是工廠方法模式的核心,全部建立對象的工廠類都必須實現該接口。(ex. 文中 IFactory
)ConstructionFactory
)固然,咱們不能爲了設計而設計,當類結構簡單的時候,咱們能夠直接使用 new
來創造,不然會增長沒必要要的代碼,反而使結構複雜化。
全部工廠模式適用場景相似:調用者無需知道他所使用的對象的類(實際上內部結構對調用者是透明的 ex. 簡單工廠)。
但仍是有所差別,如下爲我的理解:
名稱 | 適用場景 |
---|---|
簡單工廠 | 1. 工廠類負責建立的對象比較少 2. 客戶只知道傳入工廠類的參數,對於如何建立對象(邏輯)不關心 |
工廠方法 | 工廠類負責建立的對象複雜, 且內部對象層級關係比較簡單 |
抽象工廠 | 工廠類負責建立的對象複雜, 且內部對象層級關係比較複雜 |
若有錯誤和講述不恰當的地方還請指出,不勝感激!