學(xué)好設(shè)計(jì)模式防被祭天:工廠模式

工廠模式

為了防止被“殺”了祭天,學(xué)點(diǎn)設(shè)計(jì)模式,并總結(jié)下還是有必要的。

一:模式理解

  1. 工廠模式的作用是新建對象。
  2. 工廠模式的目的是讓新建對象的過程更加優(yōu)雅,減少對業(yè)務(wù)代碼的混淆。
  3. 包含簡單工廠,工廠方法,抽象工廠。
  4. 以下幾點(diǎn)可以在例子之后再看。
  5. 單獨(dú)建立用于生成對象的工程類,即為簡單工廠,類似于工具類。
  6. 在原有代碼上,抽象出一個(gè)方法,用于生成對象,即為工廠方法。
  7. 新建一個(gè)完全抽象的工廠接口/類,具體生成的對象由該工廠的實(shí)現(xiàn)類/子類決定,即為抽象工廠。


二: 例子

你是一個(gè)富二代,擁有有一家汽車公司,公司的主要工作是把汽車賣給客戶,即sellCar。

主要流程包括原料采購,組裝汽車,汽車展示。

為方便模擬,三個(gè)步驟分別為:

  1. 原料采購直接sout原料采購。
  2. 組裝汽車(assembleCar)生成對應(yīng)的汽車對象。
  3. 汽車展示調(diào)用生成汽車對象的display方法,確保生產(chǎn)正確。
富二代家的汽車公司

有一個(gè)汽車類,包含屬性為牌子,顏色,還有一個(gè)display方法。

// 汽車類
@Data
public class Car {
    private String brand;
    private String color;

    public void display() {
        System.out.println("This is a car");
    }
}

目前你司主營奔馳和寶馬車,均繼承自父類Car,對應(yīng)的類分別為:

// 寶馬車實(shí)體類
public class BMWCar extends Car{
    @Override
    public void display(){
        System.out.println("This is a BMW car");
    }
}

// 奔馳車實(shí)體類
public class BenzCar extends Car {
    @Override
    public void display(){
        System.out.println("This is a Benz car");
    }
}

兩個(gè)類沒什么區(qū)別,只是分別重寫了Car類的display方法。

你司的日常就是賣汽車(sellCar),根據(jù)不同的訂單,輸入不同的carType,新建不同的汽車對象。

public class CarCompany {
    public void sellCar(String carType) {
        System.out.println("原料采購");
        Car car = null;
        if (StringUtils.equals(carType, "BMW")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "Benz")) {
            car = new BenzCar();
        } else {
            car = new Car();
        }
        car.display();
    }

    public static void main(String[] args) {
        CarCompany carCompany = new CarCompany();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String carType = scanner.next();
            carCompany.sellCar(carType);
        }
    }
}

輸入/輸出:

BMW
原料采購
This is a BMW car
Benz
原料采購
This is a Benz car
car
原料采購
This is a car

可以看出,你司的業(yè)務(wù)還是挺簡單粗暴,只需判斷輸入生產(chǎn)不同的車即可。

作為富二代的你并不打算對其做出什么改變。

然而,有一天,你發(fā)現(xiàn)奧迪車賣的也不錯,準(zhǔn)備開始在自己公司賣奧迪車。

此時(shí)在業(yè)務(wù)代碼sellCar中增加一個(gè)else判斷條件。

你的公司越做越大,每天都會需要決定加入或者刪除某些車是否生產(chǎn)。

某一次,一個(gè)程序員在修改if else的過程中,不小心搞出個(gè)bug。由于習(xí)慣性復(fù)制粘貼,在應(yīng)該生產(chǎn)QQ車的時(shí)候,生產(chǎn)了一輛賓利車。

為此,你“殺”了這位程序員祭天。

有沒有辦法可以在不修改業(yè)務(wù)代碼的情況下,搞定第二步驟,即組裝汽車邏輯的修改呢?

1. 簡單工廠

將新建對象的邏輯從業(yè)務(wù)代碼中提取出來。

新建一個(gè)簡單汽車工廠類來承載新建汽車對象的功能。

public class SimpleCarFactory {
    public static Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "BMW")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "Benz")) {
            car = new BenzCar();
        } else {
            car = new Car();
        }
        return car;
    }
}

修改之后,汽車工廠的代碼變?yōu)椋?/p>

public class CarCompany {
    public void sellCar(String carType) {
        // 原料采購
        System.out.println("原料采購");
        // 組裝汽車
        Car car = SimpleCarFactory.assembleCar(carType);
        // 展示汽車
        car.display();
    }

    public static void main(String[] args) {
        CarCompany carCompany = new CarCompany();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String carType = scanner.next();
            carCompany.sellCar(carType);
        }
    }
}

輸入和輸出都與之前一樣。

  1. 嚴(yán)格意義上,簡單工廠并不是一種設(shè)計(jì)模式,更像是一種重構(gòu)。
  2. 重構(gòu)之后,CarCompany的sellCar代碼變得非常清晰,特別是引入方法assembleCar后,一看就可以知道該步驟是在組裝汽車。
  3. 對于剛看代碼的新手而言,就算不理解assembleCar具體操作,也可以根據(jù)該方法名知道該方法的作用。
  4. 經(jīng)過程序員的一頓改,你特別高興,自己家又多了一個(gè)工廠,又多了個(gè)廠長的身份,還高興地freestyle了一番。

2. 方法工廠

工廠的目的是生成一個(gè)對象,簡單工廠為此新建了一個(gè)類。

此外,也可以在CarCompany類下增加一個(gè)新建汽車對象的私有方法。

該方法的作用和簡單工廠一致,不同的是以方法的形式出現(xiàn),將其稱為方法工廠。

public class CarCompany {
    public void sellCar(String carType) {
        System.out.println("原料采購");
        Car car = assembleCar(carType);
        car.display();
    }

    private Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "BMW")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "Benz")) {
            car = new BenzCar();
        } else {
            car = new Car();
        }
        return car;
    }

    public static void main(String[] args) {
        CarCompany carCompany = new CarCompany();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String carType = scanner.next();
            carCompany.sellCar(carType);
        }
    }
}

當(dāng)然,如果只是將方法的實(shí)現(xiàn)移動一個(gè)位置,那也算不上是設(shè)計(jì)模式。

好不容易多了個(gè)廠長的身份,程序員竟然這么改。你頓時(shí)不高興了,回頭就準(zhǔn)備掏出自己40米的大刀。

此時(shí),程序員急忙開始解釋。

如果將CarCompany中的assembleCar方法申明為抽象方法,具體實(shí)現(xiàn)由子類來決定。

public abstract class CarCompany {
    public void sellCar(String carType) {
        System.out.println("原料采購");
        Car car = assembleCar(carType);
        car.display();
    }

    protected abstract Car assembleCar(String carType);
}

此時(shí),你完全可以開多家公司用于生產(chǎn)不同的車,它們都將繼承自CarCompany。

CarCompany類規(guī)定了sellCar方法的步驟,留下組裝汽車的方法讓不同的公司自己去實(shí)現(xiàn)。

新建兩個(gè)公司,一個(gè)賣寶馬,一個(gè)賣奔馳,分開之后,業(yè)務(wù)得到了擴(kuò)展,兩個(gè)工廠都可以生產(chǎn)轎車和SUV。

public class BMVCarCompany extends CarCompany {
    @Override
    protected Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}
public class BenzCarCompany extends CarCompany {
    @Override
    protected Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BenzCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}
public class Client {
    public static void main(String[] args) {
        CarCompany bmwCarCompany = new BMVCarCompany();
        CarCompany benzCarCompany = new BenzCarCompany();
        bmwCarCompany.sellCar("car");
        benzCarCompany.sellCar("car");
        bmwCarCompany.sellCar("suv");
        benzCarCompany.sellCar("suv");
    }
}

輸入/輸出:

原料采購
This is a BMW car
原料采購
This is a Benz car
原料采購
This is a BMW SUV car
原料采購
This is a BMW SUV car

兩個(gè)公司可以完全獨(dú)立運(yùn)行,在需要新建一個(gè)公司的時(shí)候,只需要增加新的公司類即可,如AudiCarCompany。

聽了程序員這番解釋,你雖然沒聽懂,但覺得可以一下子多搞幾個(gè)分公司,內(nèi)心還是有點(diǎn)小激動的,隨手收回了自己那把40米的大刀。

3. 抽象工廠

由于成本變動,寶馬和奔馳子公司都需要更換組裝汽車的過程,第一時(shí)間想到是直接修改兩個(gè)公司的assembleCar的代碼。

然而現(xiàn)在寶馬公司又需要生產(chǎn)紀(jì)念版的寶馬車,和普通寶馬不同的是,特別版的寶馬使用了特殊的紅色油漆。

面對這樣的需求,雖貴為富二代的你,還是準(zhǔn)備討好下程序員,以一起去徹夜鼓掌作為誘惑,希望程序員盡快完成。

在之前的步驟中,已經(jīng)將組裝汽車的過程抽象成工廠,那么更改/修改汽車組裝的過程,可以抽象成換了一家代工廠。

但無論怎么換代工廠,都要求這些工廠有assembleCar的功能,所以需要一個(gè)抽象類或者接口來約束。其中的組裝方法是抽象的,具體實(shí)現(xiàn)在子類中定義。

這就是抽象工廠這個(gè)名稱的含義。

public abstract class AbstractCarFactory {
    public abstract Car assembleCar(String carType);
}

分別新建兩家代工廠。

// 寶馬車代工廠
public class BMWCarFactory extends AbstractCarFactory {
    @Override
    public Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}

// 奔馳車代工廠
public class BenzCarFactory extends AbstractCarFactory {
    @Override
    public Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BenzCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}

兩家公司分別持有AbstractCarFactory對象,在assembleCar方法中,只需直接返回對應(yīng)工廠組裝的汽車即可,不用關(guān)注具體的工藝。

public class BMVCarCompany extends CarCompany {
    private AbstractCarFactory carFactory;

    public BMVCarCompany(AbstractCarFactory carFactory) {
        this.carFactory = carFactory;
    }

    @Override
    protected Car assembleCar(String carType) {
        return carFactory.assembleCar(carType);
    }
}
public class BenzCarCompany extends CarCompany {
    private AbstractCarFactory carFactory;

    public BenzCarCompany(AbstractCarFactory carFactory) {
        this.carFactory = carFactory;
    }

    @Override
    protected Car assembleCar(String carType) {
        return carFactory.assembleCar(carType);
    }
}
public class Client {
    public static void main(String[] args) {
        AbstractCarFactory bmwCarFactory = new BMWCarFactory();
        CarCompany bmwCarCompany = new BMVCarCompany(bmwCarFactory);
        AbstractCarFactory benzCarFactory = new BenzCarFactory();
        CarCompany benzCarCompany = new BenzCarCompany(benzCarFactory);
        bmwCarCompany.sellCar("car");
        benzCarCompany.sellCar("car");
        bmwCarCompany.sellCar("suv");
        benzCarCompany.sellCar("suv");
    }
}

當(dāng)某個(gè)工廠需要更換代工廠時(shí)候,只需新建一個(gè)繼承自AbstractCarFactory抽象工廠的具體工廠即可,如AudiCarFactory。

并將此工廠作為carCompany的屬性置入,如果只希望增加代碼而不是修改代碼,可以新建AudiCarCompany即可。

以下例子為寶馬公司更換特別版生產(chǎn)工廠之后,生產(chǎn)寶馬車的過程,在display中同時(shí)展示寶馬車的顏色。

可以看到,在更換特別版工廠之后,生產(chǎn)出來的寶馬車加上了特別的紅色。

而作為富二代的你,開上這輛特別版的寶馬,突然發(fā)現(xiàn)一下子多了好多一夜成長的機(jī)會。

此時(shí),你已經(jīng)忘了答應(yīng)給程序員的鼓掌獎勵。/(ㄒoㄒ)/~~

public class BMWCarFactoryForSpecialEdition extends AbstractCarFactory {
    @Override
    public Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        car.setColor("Red for special edition");
        return car;
    }
}
public class BMWCar extends Car {
    @Override
    public void display() {
        System.out.println("This is a BMW car with " + getColor());
    }
}

輸入/輸出:

原料采購
This is a BMW car with Red for special edition


三: 再理解

  1. 簡單工廠只是一個(gè)代碼重構(gòu)的過程,為組裝汽車的過程新建了一個(gè)類,一個(gè)靜態(tài)方法。
  2. 方法工廠可以分為簡單方法工廠和抽象方法工廠。簡單方法工廠和簡單方法差不多,只是少新建了一個(gè)工廠類。抽象方法工廠,將生成對象的方法申明為抽象,在子類中實(shí)現(xiàn),方便建立新的公司,業(yè)務(wù)拆分。
  3. 抽象工廠,定義一個(gè)什么都不做,只約定做什么的工廠,即將工廠的作用抽象出來。抽象工廠和策略模式很類似,只是抽象工廠的目的是生成對象,而策略模式的目的主要在于為不同類別的對象執(zhí)行不同的步驟。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容