一、概述
工廠模式包括簡單工廠模式(Simple Factory Pattern)
、工廠方法模式
和抽象工廠模式
,是 Java 中最常用的一類創建型設計模式,它提供了一種創建對象的最佳方式。正如它的名字,可以像工廠一樣生產出想要的東西,只要提供原料。在工廠模式中,在創建對象時不會對客戶端暴露創建邏輯,而是通過使用一個共同的接口來指向新創建的對象。簡單工廠模式是工廠方法模式的“小弟”,它不屬于 GoF 23 種設計模式,但在軟件開發中應用也較為頻繁,通常將它作為學習其他工廠模式的入門。此外,工廠方法模式還有一位“大哥”——抽象工廠模式。這三種工廠模式各具特色,難度也逐個加大,在軟件開發中它們都得到了廣泛的應用,成為面向對象程序中常用的創建對象的工具。
1??在 Java 中,通常有以下幾種創建對象的方式:
- 使用 new 關鍵字直接創建對象(最簡單,靈活性較差)
- 通過反射機制創建對象
- 通過 clone() 創建對象
- 通過工廠類創建對象
2??意圖
定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。
3??主要解決
主要解決接口選擇的問題。
4??何時使用
明確地計劃不同條件下創建不同實例時。
5??如何解決
讓其子類實現工廠接口,返回的也是一個抽象的產品。
6??關鍵代碼
創建過程在其子類執行。
7??應用實例
- 需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個汽車里面的具體實現。
- Hibernate 換數據庫只需換方言和驅動就可以。
8??優點
- 屏蔽產品的具體實現,調用者只關心產品的接口。
對調用者屏蔽具體的產品類。如果使用工廠模式,調用者只關心產品的接口就可以了,至于具體的實現,調用者根本無需關心。即使變更了具體的實現,對調用者來說沒有任何影響。 - 可以使代碼結構清晰,有效地封裝變化。在編程中,產品類的實例化有時候是比較復雜和多變的,通過工廠模式,將產品的實例化封裝起來,使得調用者根本無需關心產品的實例化過程,只需依賴工廠即可得到自己想要的產品。
- 擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。降低耦合度。產品類的實例化通常來說是很復雜的,它需要依賴很多的類,而這些類對于調用者來說根本無需知道,如果使用了工廠方法,我們需要做的僅僅是實例化好產品類,然后交給調用者使用。對調用者來說,產品所依賴的類都是透明的。
9??缺點
每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴。這并不是什么好事。
??使用場景
- 日志記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶可以選擇記錄日志到什么地方。
- 數據庫訪問,當用戶不知道最后系統采用哪一類數據庫,以及數據庫可能有變化時。
- 設計一個連接服務器的框架,需要三個協議“POP3”、“IMAP”、“HTTP”,可以把這三個作為產品類,共同實現一個接口。
1??1??注意事項
作為一種創建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復雜對象適合使用工廠模式。而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。
二、簡單工廠模式不屬于23種GOF設計模式
又叫做靜態工廠方法(StaticFactory Method)模式。 主要用于創建同一類的對象,比如創建圖形類(Rectangle 矩形、Square 正方形、Circle 圓形)
創建一個 Shape 接口和實現 Shape 接口的實現類。下一步是定義工廠類 ShapeFactory。FactoryPatternDemo,演示類使用 ShapeFactory 來獲取 Shape 對象。它將向 ShapeFactory 傳遞信息(RECTANGLE、SQUARE、CIRCLE),以便獲取它所需對象的類型。
1??創建一個接口
public interface Shape {
void draw();
}
2??創建實現接口的實現類
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
3??創建一個工廠,生成基于給定信息的實現類的對象
public class ShapeFactory {
//使用 getShape 方法獲取形狀類型的對象
public Shape getShape(String shapeType){
if("CIRCLE".equalsIgnoreCase(shapeType)){
return new Circle();
}else if("RECTANGLE".equalsIgnoreCase(shapeType)){
return new Rectangle();
}else if("SQUARE".equalsIgnoreCase(shapeType)){
return new Square();
}
return null;
}
}
4??使用該工廠,通過傳遞類型信息來獲取實現類的對象。
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//獲取 Circle 的對象,并調用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.draw();
//獲取 Rectangle 的對象,并調用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.draw();
//獲取 Square 的對象,并調用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
shape3.draw();
}
}
5??執行程序,輸出結果:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
根據傳入不同的 shapeType(如:RECTANGLE、SQUARE、CIRCLE)去創建不同的對象,然后再通過不同的 shapeType 對象去給里面的屬性賦值。無論 RECTANGLE、SQUARE、CIRCLE 都是 Shape,所以說簡單工廠模式就是用來創建同一類對象的不同實例。
三、工廠方法模式
定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。在工廠里面可以對對象的創建做嚴格的定義,client 端直接通過工廠創建出來就可以使用了。1??產品抽象類
public abstract class Product {
public abstract void method();
}
2??具體A產品
public class ProductA extends Product {
@Override
public void method() {
System.out.println("產品A");
}
}
3??具體B產品
public class ProductB extends Product {
@Override
public void method() {
System.out.println("產品B");
}
}
4??工廠抽象類
public abstract class Factory {
public abstract Product createProduct();
}
5??具體工廠
public class ProductFactory extends Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
6??客戶端使用
public class Client {
public static void main(String[] args) {
Factory factory = new ProductFactory();
Product product = factory.createProduct();
product.method();
}
}
也可以通過反射的方式
public abstract class Factory {
public abstract <T extends Product> T createProduct(Class<T> clz);
}
public class ConcreteFactory extends Factory {
@Override
public <T extends Product> T createProduct(Class<T> clz) {
Product product = null;
try {
product= (Product) Class.forName(clz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) product;
}
}
public class Client {
public static void main(String[] args) {
Factory factory = new ConcreteFactory();
Product product = factory.createProduct(ConcreteProductB.class);
product.method();
}
}
擁有多個工廠的方式稱為多工廠方法模式,如果只有一個工廠可以去掉抽象類直接定義一個 static 的方法返回對象(用 static 修飾可以不用初始化工廠直接返回產品,也可以不用 static),這種叫做工廠方法模式或者靜態工廠模式。
優點和缺點是一樣的:新加一個產品需要相應的產品類和一個工廠子類。
四、抽象工廠模式
為創建一組相關或者是相互依賴的對象提供一個接口,而不需要指定具體實現類。1??抽象工廠模式與工廠方法模式的區別
抽象工廠模式是工廠方法模式的升級版本,它用來創建一組相關或者相互依賴的對象。它與工廠方法模式的區別就在于,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則是針對的多個產品等級結構。在編程中,通常一個產品結構,表現為一個接口或者抽象類,也就是說,工廠方法模式提供的所有產品都是衍生自同一個接口或抽象類,而抽象工廠模式所提供的產品則是衍生自不同的接口或抽象類。
在上面的類圖中,兩廂車和三廂車稱為兩個不同的等級結構。而2.0排量車和2.4排量車則稱為兩個不同的產品族。再具體一點,2.0排量兩廂車和2.4排量兩廂車屬于同一個等級結構,2.0排量三廂車和2.4排量三廂車屬于另一個等級結構;而2.0排量兩廂車和2.0排量三廂車屬于同一個產品族,2.4排量兩廂車和2.4排量三廂車屬于另一個產品族。
明白了等級結構和產品族的概念,就理解工廠方法模式和抽象工廠模式的區別了,如果工廠的產品全部屬于同一個等級結構,則屬于工廠方法模式;如果工廠的產品來自多個等級結構,則屬于抽象工廠模式。在本例中,如果一個工廠模式提供2.0排量兩廂車和2.4排量兩廂車,那么屬于工廠方法模式;如果一個工廠模式是提供2.4排量兩廂車和2.4排量三廂車兩個產品,那么這個工廠模式就是抽象工廠模式,因為提供的產品是分屬兩個不同的等級結構。當然,如果一個工廠提供全部四種車型的產品,因為產品分屬兩個等級結構,當然也屬于抽象工廠模式了。
2??Q3 和 Q7 有不同的輪胎、發動機、制動系統。雖然生產的零件不同,型號不同。但是根本上都有共同的約束,就是輪胎、發動機、制動系統。設計如下:
- 需要一個抽象工廠,里面有三個接口分別為生產輪胎、發動機、制動系統,抽象類。
- 需要三個抽象產品分別為輪胎、發動機、制動系統,抽象接口。
- 需要實現上面的三個抽象接口,定義出每個接口不同的對象,比如:普通輪胎和越野輪胎。
- 需要兩個具體類繼承自上面的抽象類,實現具體的工廠,比如:生產 Q3 的工廠和生產 Q7 的工廠。
- 在客戶端 new 出對應的具體工廠并調用對應的生產方法。
三個產品抽象接口
public interface ITire {
void tire();//輪胎
}
public interface IEngine {
void engine();//發動機
}
public interface IBrake {
void brake();//制動系統
}
根據抽象接口定義不同的對象
public class NormalBrake implements IBrake{
@Override
public void brake() {
System.out.println("普通制動");
}
}
public class SeniorBrake implements IBrake{
@Override
public void brake() {
System.out.println("高級制動");
}
} //后面的定義省略。。。。。。。。。。。。。
抽象工廠
public abstract class CarFactory {
/**
* 生產輪胎
* @return 輪胎
* */
public abstract ITire createTire();
/**
* 生產發動機
* @return 發動機
* */
public abstract IEngine createEngine();
/**
* 生產制動系統
* @return 制動系統
* */
public abstract IBrake createBrake();
}
實現具體的工廠類
public class Q3Factory extends CarFactory{
@Override
public ITire createTire() {
return new NormalTire();
}
@Override
public IEngine createEngine() {
return new DomesticEngine();
}
@Override
public IBrake createBrake() {
return new NormalBrake();
}
}
客戶端使用
public class Client {
public static void main(String[] args) {
//A車廠
CarFactory factoryQ3 = new Q3Factory();
factoryQ3.createTire().tire();
factoryQ3.createEngine().engine();
factoryQ3.createBrake().brake();
System.out.println("---------------");
}
}
//輸出
普通輪胎
國產發動機
普通制動
五、區別
1??工廠方法模式:只有一個抽象產品類,具體工廠類只能創建一個具體產品類的實例。
2??抽象工廠模式:有多個抽象產品類 ,具體工廠類能創建多個具體產品類的實例。