在生活中,我們用的所有東西都是通過工廠生產出來,比如:手機,汽車,電腦等。在 面向對象的世界里,對象也是可以通過工廠來創建的。今天我們就來談論一下創建型模式--工廠設計模式。
1. 案例背景
設計模式一般比較抽象,為了方便理解,我們首先來引用一個生活中的例子-- 消費者去 汽車城或者4s 店 買汽車。這里有一下對象:1. 消費者; 2. 汽車銷售方。 3. 汽車。
2. 不用設計模式
這里我們首先來分析 不使用 設計模式的情況下,代碼的實現方式
- Car 接口
生活中 汽車銷售方一般是4s 店或者汽車經銷商,一般都會銷售不同品牌或者型號的汽車,但是具體是啥汽車我們不知道,所以設計一個Car 接口,來抽象一下汽車的型號或者品牌
public interface Car {
/**
* 駕駛
汽車就一個方法,被被人駕駛
*/
void drive();
}
- 汽車
汽車接口設計好了,接下來我們就來設計一下具體的汽車,假設 是 寶馬和奧迪
public class BMWCar implements Car {
@Override
public void drive() {
System.out.println("駕駛 BMW !");
}
}
public class AODICar implements Car {
@Override
public void drive() {
System.out.println(" 駕駛 奧迪 !");
}
}
- 汽車銷售對象
汽車有了,接下來我們來設計 汽車銷售對象。假設該能售賣 寶馬和奧迪
public class SaleCarStore {
public Car sale(String carName) {
Car car = null;
if ("BMW".equals(carName)) {//寶馬汽車
car = new BMWCar();
}
if ("AODI".equals(carName)) {// 奧迪汽車
car = new AODICar();
}
return car;
}
}
汽車銷售方就一個方法,就是 sale 汽車。我們沒有使用設計模式的情況下,一般是自己 new 對象。當然實際生活中一般都是 汽車工廠生產好汽車,交給4s 店或者其他經銷商來賣。后面我們再來分析使用 汽車工廠 的好處。
- 消費者
接下來我們只需要 設計一個 消費者對象,通過 汽車消費方去購買汽車即可。
public class CarCustomer {
private SaleCarStore saleCarStore ;
CarCustomer(SaleCarStore saleCarStore) {//消費者汽車售賣對象買車
this.saleCarStore = saleCarStore;
}
public Car getCar(String carName) {
Car car = saleCarStore.sale(carName);
return car;
}
/**
* 駕駛 買完車之后就 自己開車走了
* @param car
*/
public void drive(Car car) {
car.drive();
}
public static void main(String[] args) {
SaleCarStore saleCarStore = new SaleCarStore();
CarCustomer carCustomer = new CarCustomer(saleCarStore );// 消費者 通過 SaleCarStore 對象買車
Car bwm = carCustomer.getCar("BMW");// 假設購買的是 寶馬
carCustomer.drive(bwm);
}
上面是我們在沒有使用 工廠設計模式下的代碼。消費者成功的購買到了汽車,并開走了。這時候,又有一個消費者要來買汽車了,但是他嫌寶馬和奧迪都太貴了,要買五菱宏光,但是這時候 汽車銷售對象,沒有五菱宏光,但是上面的生意不可能不做吧,于是就連夜趕工 搞到了一臺。于是 SaleCarStore 對象就必須要修改:
public class SaleCarStore {
public Car sale(String carName) {
Car car = null;
if ("BMW".equals(carName)) {//寶馬汽車
car = new BMWCar();
}
if ("AODI".equals(carName)) {// 奧迪汽車
car = new AODICar();
}
// add
if ("WLHG".equals(carName)) {// 五菱宏光
car = new WLHGCar();
}
return car;
}
}
----- 購買
public static void main(String[] args) {
SaleCarStore car4sStore = new SaleCarStore();
CarCustomer carCustomer = new CarCustomer(car4sStore);
Car bwm = carCustomer.getCar("WLHG");
carCustomer.drive(bwm);
}
好了,錢賺到了。生意越來越好,接下來,買各種各樣的車的人都有,比如 思域啊,邁銳寶XL 啊,奧迪A4L 啊等等。那么我們這時候如果在原來的基礎上 滿足消費者的需求就要不斷 的 添加 如下類似的代碼:
// add .......
if ("WLHG".equals(carName)) {// 五菱宏光
car = new WLHGCar();
}
// add .......
這時候,老板就發現,自己不能再這樣搞下去了,雖然錢賺到了,自己卻累的要死。于是,就想到一個主意:顧客要來買車,就先下個單子,拿到這個單子以后,去聯系 汽車生產廠商,然他們去生產汽車,自己中間賺差價就好了。于是,代碼就可以改成下面的樣子:
- 汽車工廠--簡單工廠
public class SimpleCarFactory {
public Car createCar(String carName) {
Car car = null;
if ("BMW".equals(carName)) {//寶馬汽車
car = new BMWCar();
}
if ("AODI".equals(carName)) {// 奧迪汽車
car = new AODICar();
}
if ("WLHG".equals(carName)) {// 五菱宏光
car = new WLHGCar();
}
return car;
}
}
----- SaleCarStore
public class SaleCarStore {
private SimpleCarFactory simpleCarFactory;
SaleCarStore(SimpleCarFactory simpleCarFactory) {
this.simpleCarFactory = simpleCarFactory;
}
public Car sale(String carName) {
// Car car = null;
//// if ("BMW".equals(carName)) {//寶馬汽車
//// car = new BMWCar();
//// }
//// if ("AODI".equals(carName)) {// 奧迪汽車
//// car = new AODICar();
//// }
//// // add
//// if ("WLHG".equals(carName)) {// 五菱宏光
//// car = new WLHGCar();
//// }
// 簡單工廠創建Car 對象
Car car = simpleCarFactory.createCar(carName);
return car;
}
}
---- CarCustomer#main
public static void main(String[] args) {
// 簡單工廠
SimpleCarFactory simpleCarFactory = new SimpleCarFactory();
SaleCarStore saleCarStore = new SaleCarStore(simpleCarFactory);
CarCustomer carCustomer = new CarCustomer(saleCarStore);
Car bwm = carCustomer.getCar("WLHG");
carCustomer.drive(bwm);
}
好了,現在 汽車經銷商老板 輕松了,不管顧客需要什么樣的汽車,都可以交給 SimpleCarFactory 工廠來生產了。
注意:簡單工廠模式原本是不屬于常用23中設計模式中的,在《Head First》中有解釋,上面的案例也是從 這本書中演變來的,書中是 披薩 案例。
- 工廠方法
承接上一個案例,雖然顧客對汽車的需求加大,各種汽車都要滿足,于是 SimpleCarFactory 汽車工廠 必須擴展自己的 createCar 方法,隨著工廠規模的發展出現了以下代碼:
public Car createCar(String carName) {
Car car = null;
if ("BMW".equals(carName)) {//寶馬汽車
car = new BMWCar();
}
if ("AODI".equals(carName)) {// 奧迪汽車
car = new AODICar();
}
if ("WLHG".equals(carName)) {// 五菱宏光
car = new WLHGCar();
}
// add if()........
// add if()........
// add if()........
return car;
}
我們發現,要是無限添加 if ,createCar 方法會變得難以維護。作為工廠老板,就想了一個辦法,把 createCar ()拆開,分成不同的生產小組,生產各自的汽車,于是 代碼就變成了這樣:
public class CarFactoryMethod {
//生產寶馬
public BMWCar createBMWCar() {
return new BMWCar();
}
// 生產奧迪
public AODICar createAODICar() {
return new AODICar();
}
//生產五菱宏光
public WLHGCar createWLHGCar() {
return new WLHGCar();
}
}
上面這個模式就被叫做 工廠方法。如果,現在新增一個汽車,只需要添加一個 方法,來創建即可。
- 抽象工廠
隨著工廠的發展,寶馬車出現了不同的車系,如X1,X6 等,奧迪出現了 A3,A4L,A6L等,于是,寶馬和奧迪工廠就獨立出來了,單獨生產這幾種車系。
public class BMWFactoryMethod {
public BMWCarX1 X1() {
return new BMWCarX1();
}
public BMWCarX5 X5() {
return new BMWCarX5();
}
}
------- 奧迪車系
public class AODIFactoryMethod {
public AODICarA3 aodiCarA3() {
return new AODICarA3();
}
public AODICarA4L aodiCarA4L() {
return new AODICarA4L();
}
}
首先,要說明一個問題,抽象工廠,不是對代碼的抽象,也就說 在代碼里面 我們不需要將工廠 用 abstract 修飾,抽象工廠,是從 《Head First》中翻譯過來的,抽象工廠 是對 工廠方法的延伸,抽象工廠可以生產 一系列相關的產品,比如 寶馬系列,奧迪系列。這里就是 國內經常有人說的 產品到 產品族的概念。注意,這里為了描述簡單,并沒有 抽取接口,重要的是 編程思想, 我們從 工廠方法,生產單個汽車產品,到 擴展到一個工廠可以生產 一系列相關的系列汽車產品,也就是 從 工廠方法 擴展到了 抽象工廠。那么抽象 就應該是對 事物擴展的抽象了,而不是具體對 代碼的抽象,并不強制你對代碼進行抽象。比如,擴展寶馬車系,對不可預見的車系進行抽象,因為我并不知道,下一個寶馬車系是啥,但是一旦確定了,就可以直接在工廠中添加方法創建即可,所以,這里的抽象應該是對事物的抽象。
在我們明白了產品到 產品系列的概念后,可以對抽象工廠 進行接口設計。
- CarFactory
/**
* car 工廠定義
*/
public interface CarFactory {
Car createCar(String type);
}
----- car
public class BMWCarX1 implements Car {
public BMWCarX1() {
System.out.println("X1");
}
@Override
public void drive() {
System.out.println("drive X1");
}
}
public class BMWCarX5 implements Car {
public BMWCarX5() {
System.out.println("X5");
}
@Override
public void drive() {
System.out.println("drive X5");
}
}
------- BMWCarFactoryMethod 抽象工廠
public class BMWCarFactoryMethod implements CarFactory {
@Override
public Car createCar(String type) {
switch (type) {
case "X1":
return bmwCarX1();
case "X5":
return bmwCarX5();
}
return null;
}
private BMWCarX1 bmwCarX1() {
return new BMWCarX1();
}
private BMWCarX5 bmwCarX5() {
return new BMWCarX5();
}
}
到這里我們就分析了 工廠模式中的 簡單工廠,工廠方法和抽象工廠的設計思想。我們可以發現一個問題,工廠模式是不符合 開閉原則的,但是比較符合單一職責原則,工廠模式實現方式可以有多中,不必糾結固定的實現方式。