學好設計模式防被祭天:工廠模式

工廠模式

為了防止被“殺”了祭天,學點設計模式,并總結下還是有必要的。

一:模式理解

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


二: 例子

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

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

為方便模擬,三個步驟分別為:

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

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

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

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

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

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

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

兩個類沒什么區別,只是分別重寫了Car類的display方法。

你司的日常就是賣汽車(sellCar),根據不同的訂單,輸入不同的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

可以看出,你司的業務還是挺簡單粗暴,只需判斷輸入生產不同的車即可。

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

然而,有一天,你發現奧迪車賣的也不錯,準備開始在自己公司賣奧迪車。

此時在業務代碼sellCar中增加一個else判斷條件。

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

某一次,一個程序員在修改if else的過程中,不小心搞出個bug。由于習慣性復制粘貼,在應該生產QQ車的時候,生產了一輛賓利車。

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

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

1. 簡單工廠

將新建對象的邏輯從業務代碼中提取出來。

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

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;
    }
}

修改之后,汽車工廠的代碼變為:

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

2. 方法工廠

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

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

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

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);
        }
    }
}

當然,如果只是將方法的實現移動一個位置,那也算不上是設計模式。

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

此時,程序員急忙開始解釋。

如果將CarCompany中的assembleCar方法申明為抽象方法,具體實現由子類來決定。

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

    protected abstract Car assembleCar(String carType);
}

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

CarCompany類規定了sellCar方法的步驟,留下組裝汽車的方法讓不同的公司自己去實現。

新建兩個公司,一個賣寶馬,一個賣奔馳,分開之后,業務得到了擴展,兩個工廠都可以生產轎車和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

兩個公司可以完全獨立運行,在需要新建一個公司的時候,只需要增加新的公司類即可,如AudiCarCompany。

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

3. 抽象工廠

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

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

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

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

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

這就是抽象工廠這個名稱的含義。

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方法中,只需直接返回對應工廠組裝的汽車即可,不用關注具體的工藝。

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");
    }
}

當某個工廠需要更換代工廠時候,只需新建一個繼承自AbstractCarFactory抽象工廠的具體工廠即可,如AudiCarFactory。

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

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

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

而作為富二代的你,開上這輛特別版的寶馬,突然發現一下子多了好多一夜成長的機會。

此時,你已經忘了答應給程序員的鼓掌獎勵。/(ㄒ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. 簡單工廠只是一個代碼重構的過程,為組裝汽車的過程新建了一個類,一個靜態方法。
  2. 方法工廠可以分為簡單方法工廠和抽象方法工廠。簡單方法工廠和簡單方法差不多,只是少新建了一個工廠類。抽象方法工廠,將生成對象的方法申明為抽象,在子類中實現,方便建立新的公司,業務拆分。
  3. 抽象工廠,定義一個什么都不做,只約定做什么的工廠,即將工廠的作用抽象出來。抽象工廠和策略模式很類似,只是抽象工廠的目的是生成對象,而策略模式的目的主要在于為不同類別的對象執行不同的步驟。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容