源碼中的設計模式--工廠模式

本文要解決的幾個問題,

1、什麼是工廠模式

2、工廠模式分爲哪幾種

3、工廠模式的優點

4、源碼中有哪些地方使用了工廠模式

一、模式入場

  看到標題想必小夥伴很好理解,所謂“工廠模式”從字面上就可以理解,比亞迪工廠的作用不就是生產比亞迪汽車的,在java中的工廠模式就是用來產生實例的。現在我有這樣一個類,

Car.java

package com.my.factory;
/**
 * 汽車類
 * @date 2022/5/8 11:15
 */
public class Car {
    //汽車唯一編碼
    private String code;
    //汽車型號
    private String model;
}

現在要使用Car生成一個具體的實例,那麼平時的做法肯定是new了,如下

Car car=new Car();

  現在有這樣一個場景,在很多地方都要使用Car的實例,那麼每一次使用都new一次,都是重複的代碼從代碼規範層面上就不好看,而且這也不符合設計原則。我們是不是可以專門有一個類來生成Car的實例,在需要Car實例的地方只需要調用該類的方法即可,爲此有了下面的工具類,

package com.my.factory.simple;

import com.my.factory.Car;
/**
 * 簡單Car工廠
 * @date 2022/5/8 11:22
 */
public class SimpleCarFactory {
    public Car productCar(){
        return new Car();
    }
}

現在想生成Car,就不再使用new了,我調用工具類就可以了,

SimpleCarFactory carFactory=new SimpleCarFactory();
Car car=carFactory.productCar();

  有了SimpleCarFactory工具類就好多了,調用其productCar()方法就給我返回Car實例了,擺脫了new的方式,再也不用被隔壁的小姐姐嘲笑只會new了。

  上面提到的SimpleCarFactory工具類,其實就是工廠模式的一種實現,給它起個名字叫“簡單工廠”,《Head first 設計模式》一書中給出的釋義是

簡單工廠其實不是一個設計模式,反而比較像是一種編程習慣。但由於經常被使用,所以我們給它一個“Head First Pattern榮譽獎”。

上面說了簡單工廠更像是一種編程習慣,不過這裏我也把它看作是工廠模式的一種實現方式。

  在使用了一段SimpleCarFactory類後,有小夥伴提出每次都需要new一個SimpleCarFactory的實例才能調用其productCar()方法,既然是工具類,把productCar()方法聲明爲static不是更好,的確在設計理念上又進了一步,

package com.my.factory.simple;

import com.my.factory.Car;
/**
 * 簡單Car工廠
 * @date 2022/5/8 11:22
 */
public class SimpleCarFactory {
    public static Car productCar(){
        return new Car();
    }
}

再使用的時候只需這樣用就好了,

Car car=SimpleCarFactory.productCar();

上面的這種方式給它起個名字叫“簡單靜態工廠”,不知不覺中又會了另一種實現,可以和隔壁的小姐姐去炫耀一番了。

“簡單工廠”和“簡單靜態工廠”都是有一個專門的類來生成實例,區別是後者的方法是靜態的。

二、深入工廠模式

  上面說到的無論是“簡單工廠”還是“簡單靜態工廠”其實本質上都是一樣的,都是在一個類中生成類的實例。

2.1、工廠方法模式

  還是拿上面的汽車工廠的例子來舉例,有這樣的一個場景,由於汽車訂單激增,一個工廠已經無法完成訂單了,必須要新建一個工廠來生產汽車,而且每個工廠可以生產不同類型的汽車,現在要對上面的SimpleCarFactory和Car進行改造。假設有兩個工廠分別是ConcreteCarFactoryOne和ConcreteCarFactoryTwo,生產的汽車有Biyadiar、XiandaiCar等,現在的類圖如下,

ConcreteCarFactoryOne.java

package com.my.factory.concrete;

import com.my.factory.BiyadiCar;
import com.my.factory.ConcreteCar;
/**
 * 生產比亞迪汽車的工廠
 * @date 2022/5/8 16:17
 */
public class ConcreteCarFactoryOne extends ConcreteCarFactory {

    @Override public ConcreteCar productCar() {
        car = new BiyadiCar();
        car.setCode("1");
        car.setModel("byd");
        return car;
    }
}

ConcreteCarFactoryTwo.java

package com.my.factory.concrete;

import com.my.factory.ConcreteCar;
import com.my.factory.XiandaiCar;

/**
 * 生產現代汽車的工廠
 * @date 2022/5/8 16:42
 */
public class ConcreteCarFactoryTwo extends ConcreteCarFactory {
    @Override public ConcreteCar productCar() {
        car = new XiandaiCar();
        car.setCode("2");
        car.setModel("xiandai");
        return car;
    }
}

好了,兩個工廠類已經完成了,分別生成比亞迪汽車和現代汽車,細心的小夥伴發現一個問題,這兩個工廠都有productCar()方法,可不可以抽取出來,答案是必須抽出來,我這裏抽取爲抽象類,讓ConcreteCarFactoryOne和ConcreteCarFactoryTwo分別進行實現,

ConcreteCarFactory.java

package com.my.factory.concrete;

import com.my.factory.ConcreteCar;

/**
 * 抽象工廠
 * @date 2022/5/8 16:46
 */
public abstract class ConcreteCarFactory {
    //要生產的汽車,由子類進行初始化
    protected ConcreteCar car;

    //由子類實現該方法
    protected abstract ConcreteCar productCar();

    //給汽車噴漆
    public void sprayPaint() {
        System.out.println("給--" + car + "--噴漆");
    }
}

ConcreteCarFactoryOne和ConcreteCarFactoryTwo的修改不再貼出,聰明的你肯定知道怎麼改。

另外,對於汽車類這裏也抽出了一個公共類,BiyadiCar和XiandaiCai會繼承改類,

ConcreteCar.java

package com.my.factory;

/**
 * 汽車接口
 * @date 2022/5/8 16:21
 */
public class ConcreteCar {
    protected String code;
    protected String model;

    //省略get/set方法
}

BiyadiCar.java

package com.my.factory;

/**
 * 比亞迪汽車
 * @date 2022/5/8 16:18
 */
public class BiyadiCar extends ConcreteCar{
    @Override public String toString() {
        return "BiyadiCar{" + "code='" + code + '\'' + ", model='" + model + '\'' + '}';
    }
}

XiandaiCar這裏就不再給出類似的代碼。

下面看下測試類,

TestConcreteCarFactory.java

package com.my.factory.concrete;

import com.my.factory.ConcreteCar;

/**
 * 測試類
 * @date 2022/5/8 16:57
 */
public class TestConcreteCarFactory {
    public static void main(String[] args) {
        ConcreteCarFactory biyadaCarFactory = new ConcreteCarFactoryOne();
        ConcreteCar biyadiCar = biyadaCarFactory.productCar();
        biyadaCarFactory.sprayPaint();

        ConcreteCarFactory xiandaiCarFactory = new ConcreteCarFactoryTwo();
        ConcreteCar xiandaiCar = xiandaiCarFactory.productCar();
        xiandaiCarFactory.sprayPaint();
    }
}

測試結果可以看到創建了兩個不同的汽車

給--BiyadiCar{code='1', model='byd'}--噴漆
給--XiandaiCar{code='2', model='xiandai'}--噴漆

其UML圖如下

上面便是工廠模式的工廠方法模式的實現,《Head frist 設計模式》一書中對此模式給出的釋義是

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

從上面的UML圖中可以很好的理解上面的話,”一個創建對象的接口“這裏指的不但但是interface,在本例中定義的則是一個抽象方法。同時類的實例化是在具體的子類中實現的,到底要實例化什麼樣的類則要根據相應的工廠來決定。

2.2、抽象工廠模式

  前面我們創建了兩個工廠,都是用來生產汽車的,唯一的區別是生產的汽車是不一樣的。現在有這樣的場景,一個工廠僅生產汽車太浪費資源了,現在新能源是發展的趨勢,每個工廠再上一條生產線生產電池吧,爲此,上面的工廠需要提供一個接口來生產汽車和電池,這次我們不使用抽象類了,使用接口,

package com.my.factory.concrete.factory;

import com.my.factory.Battery;
import com.my.factory.ConcreteCar;

/**
 * 生產汽車和電池的接口
 * @date 2022/5/8 18:46
 */
public interface ConcreteFactory {
    //生產汽車
    ConcreteCar productCar();
    //生產電池
    Battery productBattery();
}

上面的接口中有兩個方法一個生產汽車一個生產電池,相應的實現類也必須實現這兩個方法。這種一個接口中包含多個生成實例的模式稱爲”抽象工廠模式“,《Head first 設計模式》一書中給出的釋義是,

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

該釋義說的很清楚,注意”家族“二字,說的就是包含一個以上的方法,後續”不需要明確指定具體類“,則是要在具體使用的時候選擇合適的實現即可。

三、追尋源碼

3.1、mybatis中的SqlSessionFactory

在mybatis中的SqlSessionFactory,便是工廠模式的一種體現,更確切的說是抽象工廠模式,

在SqlSessionFactory中有openSession方法,且該方法有多個重載,並且還有一個getConfiguration方法,下面看起具體的實現類,

共有兩個實現分別是DefaultSqlSessionFactory和SqlSessionManager,看下openSession()方法在DefaultSqlSesssionFactory中的實現

最終返回的是一個SqlSession的實現DefaultSqlSession,和上面的工廠模式的UML神奇的類似。

3.2、Spring中的BeanFactory

在spring中有BeanFactory接口,

可以看到該接口中有getBean、getType、getAliases方法,這些都可以作爲抽象工廠的證據,小夥伴們說了還有isSingleton、containsBean方法,這些我們說不能作爲工廠模式的證據,因爲,工廠模式的定義是要生成實例,也就是說工廠模式要創建並返回一個類的實例,而isSingleton、containsBean沒有創建實例。看下該接口的實現

在其實現中有AbstractBeanFactory實現,其getBean方法的一個實現如下,

  由於spring實現的太複雜,這裏不再詳述。有小夥伴會問沒看到在實現中有new啊,的確沒有,在spring的實現中是通過反射的方式創建的,我們說生成類的實例不僅只有new的方式,工廠模式的關鍵在於生成類的實例,而不在於如何生成。

 

四、總結

  總結下工廠模式的要點,

  1、工廠模式分爲簡單工廠、簡單靜態工廠、工廠方法、抽象工廠四種不同的實現;

  2、工廠模式的使用原則在於如何創建類的實例,可以使用new,也可以使用反射、反序列化等方式;

  3、工廠模式擺脫了使用者傳統的new的方式,讓對象的創建集中在一處,對設計進行了解耦,讓使用者不必關心創建對象的細節,只需使用接口;

 

  今天的分享就到這裏了,小夥伴們回想下文章開頭提的幾個問題都有答案了麼,沒有的話多讀幾遍哦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章