設計之禪——工廠方法
一、工廠概覽
我們生活中有許許多多的工廠,為商家提供產品,而我們開發者口中所談論的工廠方法模式產生靈感也是來源於此,結合生活中的例項我們可以非常輕鬆的理解該模式。它是一種建立型設計模式,也是專案開發中用的最多的設計模式之一,用於物件的建立。
二、走進工廠
1. 簡單工廠
在我們平時開發時,常常會用到“new”來建立例項,但你是否想過所有的物件都通過我們自己手動建立存在什麼問題?比如你的程式碼中嵌入了類似下面這樣的程式碼:
Car car;
if (benz) {
car = new Benz();
} else if (bmw) {
car = new Bmw();
}
如果你對設計原則有一定的瞭解的話,應該明白這已經違背了依賴倒置
public abstract class Car {
abstract void makeHead();
abstract void makeBody();
abstract void makeTail();
public void assemble() {
System. out.println("Assemble all component to car!");
}
}
首先抽象出car類,所有具體品牌的車繼承該抽象類,這一“家族”就是我們需要的產品。接著,我們將建立者的程式碼封裝為一個簡單工廠,客戶端則通過該工廠來獲取產品例項:
public class CarFactory {
public static Car getCar(String type) throws Exception {
if (CarType.BENZ.equals(type)) {
return new Benz();
} else if (CarType.BMW.equals(type)) {
return new Bmw();
} else {
throw new Exception("No such car!");
}
}
}
public class MainClass {
public static void main(String[] args) throws Exception {
Car car = CarFactory.getCar(CarType.BENZ);
car.makeHead();
car.makeBody();
car.makeTail();
car.assemble();
}
}
這就是簡單工廠的實現,將可能會變化的部分獨立出來,增加新的產品時也不用再去修改客戶端的程式碼,但是在簡單工廠方法實現部分仍然違背了OPEN-CLOSE原則,這該如何解決呢?
public static Car getCar2(String className) throws Exception {
Class<?> clzz = Class.forName(className);
return (Car) clzz.newInstance();
}
public static void main(String[] args) throws Exception {
Car benz = CarFactory.getCar2("cn.dark.simplefactory.Benz");
}
我們只需要利用反射就可以了,這樣,簡單工廠類就不用再通過型別去判斷了,客戶端只需要傳入所需產品的完全限定名就行,至此,我們增加新的產品時也不用再修改任何的程式碼了,全部由簡單工廠為我們建立例項,可以說是萬能的(什麼都能生產)。但是,生活中是沒有萬能工廠的,程式碼裡也不存在完美的設計模式。使用簡單工廠模式,客戶端必須要傳入正確的型別,一旦傳入錯誤就得不到想要的產品了,並且使用反射也會使程式效能下降。所以簡單工廠模式在《Head First設計模式》書中也被作者定義為並非一種真正的設計模式,而只是一種編碼習慣。下面我們就來實現一個真正的工廠模式。
2. 工廠方法模式
工廠方法模式定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法讓類把例項化推遲到子類。
根據工廠方法模式的定義我們可以瞭解到我們需要定義一個工廠的介面,然後為每一個具體的產品定義一個工廠並實現自工廠介面,就好比現實生活中,生產汽車的工廠就只生產汽車,生產自行車的工廠就只生產自行車,這樣,每當增加一個新的產品時,我們就只需要再增加一個對應的工廠,而不用再修改以前的類,也符合了OPEN-CLOSE原則。程式碼如下:
public abstract class Vehicle {
abstract void makeHead();
abstract void makeBody();
abstract void makeTail();
public void assemble() {
System.out.println("Assemble all component to vehicle!");
}
}
public class Car extends Vehicle {
@Override
void makeHead() {
System.out.println("Car's head is created!");
}
@Override
void makeBody() {
System.out.println("Car's body is created!");
}
@Override
void makeTail() {
System.out.println("Car's tail is created!");
}
}
public class Bike extends Vehicle {
@Override
void makeHead() {
System.out.println("Bike's head is created!");
}
@Override
void makeBody() {
System.out.println("Bike's body is created!");
}
@Override
void makeTail() {
System.out.println("Bike's tail is created!");
}
}
上面是產品家族,接著是工廠家族:
public interface Factory {
Vehicle getCar();
}
public class BikeFactory implements Factory {
@Override
public Vehicle getCar() {
return new Bike();
}
}
public class CarFactory implements Factory {
@Override
public Vehicle getCar() {
return new Car();
}
}
public class MainClass {
public static void main(String[] args) {
Factory factory = new BikeFactory();
Vehicle benz = factory.getCar();
factory = new CarFactory();
Vehicle bmw = factory.getCar();
}
}
這就是一個工廠模式的簡單實現,看起來這就相當完美了,但是如果一個工廠不只是生產一類產品,而是一組具有同一屬性的產品,那工廠模式就不那麼適用了,於是,就有了抽象工廠模式。
3. 抽象工廠模式
如上所說,在我們現實生活中往往也是一個工廠會生產多類產品,但這些產品都屬於一個品牌下,比如,賓士工廠會生產汽車,也可能會生產自行車,或者是電動車等等,寶馬工廠也是一樣。這就需要對工廠進行抽象,而不再是產品。對於上述程式碼我們可以進行如下的改進,首先還是產品家族:
public interface Vehicle {
}
public abstract class Car implements Vehicle {
}
public abstract class Bike implements Vehicle {
}
public class BenzBike extends Bike {
}
public class BenzCar extends Car {
}
public class BmwBike extends Bike {
}
public class BmwCar extends Car {
}
然後是工廠家族:
public interface Factory {
Car getCar();
Bike getBike();
}
public class BmwFactory implements Factory {
@Override
public Car getCar() {
return new BmwCar();
}
@Override
public Bike getBike() {
return new BmwBike();
}
}
public class BenzFactory implements Factory {
@Override
public Car getCar() {
return new BenzCar();
}
@Override
public Bike getBike() {
return new BenzBike();
}
}
通過程式碼我們可以明顯的發現工廠模式和抽象工廠的區別在哪裡,工廠模式中建立工廠的每一個實現對應於每一類產品,一個工廠就只產生這一類產品;而抽象工廠模式中每一個具體的工廠則是對應於具有同一屬性的多類產品,每一個工廠都可生產一族產品,但若是從產品種類新增的角度來講又違背了open-close原則。因此,我們在實際應用中應當根據具體情況而使用,往往一般都是互相搭配使用。最後再看看《Head First設計模式》對於抽象工廠的定義:
抽象工廠模式提供一個介面,用於建立相關或依賴物件的家族,而不需要明確指定具體類。
三、總結
本篇講述了簡單工廠、工廠模式以及抽象工廠模式的具體實現,該類模式在我們實際編碼中也是非常常用的,因此瞭解掌握是必要的。專案完整程式碼請訪問小編github。