設計模式之一---工廠方法模式【Factory Method模式】
在設計模式中,Factory Method模式是一種比較簡單的設計模式,應用比較廣泛,但也是一種比較重要的設計模式之一。在很多地方我們都會看到xxxFactory這樣命名的類,那麼,什麼是Factory Method,為什麼要用這個模式,如何用Java語言來實現該模式?
【1】基本概念
FactoryMethod是一種建立性模式,它定義了一個建立物件的介面,但是卻讓子類來決定具體例項化哪一個類.當一個類無法預料要建立哪種類的物件或是一個類需要由子類來指定建立的物件時我們就需要用到Factory Method
模式了.簡單說來,Factory Method可以根據不同的條件產生不同的例項,當然這些不同的例項通常是屬於相同的型別,具有共同的父類.Factory Method把建立這些例項的具體過程封裝起來了,簡化了客戶端的應用,也改善了程式的擴充套件性,使得將來可以做最小的改動就可以加入新的待建立的類. 通常我們將Factory Method作為一種標準的建立物件的方法,當發現需要更多的靈活性的時候,就開始考慮向其它建立型模式轉化。
【2】簡單分析
我們先來看一下該設計模式的UML圖:
上圖是Factory Method 模式的結構圖,讓我們可以進行更方便的描述:
- Product: 需要建立的產品的抽象類.
- ConcreteProduct: Product的子類,一系列具體的產品.
- Creator: 抽象建立器介面,宣告返回Product型別物件的Factory Method.
- ConcreteCreator: 具體的建立器,重寫Creator中的Factory Method,返回ConcreteProduct型別的例項.
同時可以清楚的看出這樣的平行對應關係: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象產品對應抽象建立器,具體產品對應具體建立器.這樣做的好處是什麼呢?為什麼我們不直接用具體的產品和具體的建立器完成需求呢?實際上我們也可以這樣做.但通過Factory Method模式來完成,客戶(client)只需引用抽象的Product和Creater,對具體的ConcreteProduct和ConcreteCreator可以毫不關心,這樣做我們可以獲得額外的好處:
-
首先客戶端可以統一從抽象建立器獲取產生的例項,Creator的作用將client和產品建立過程分離開來,客戶不用操心返回的是那一個具體的產品,也不用關心這些產品是如何建立的.同時,ConcreteProduct也被隱藏在Product後面,ConreteProduct繼承了Product的所有屬性,並實現了Product中定義的抽象方法,按照Java中的物件造型(cast)原則,通過ConcreteCreator產生的ConcreteProduct可以自動的上溯造型成Product.這樣一來,實質內容不同的ConcreteProduct就可以在形式上統一為Product,通過Creator提供給client來訪問.
- 其次,當我們新增一個新的ConcreteCreator時,由於Creator所提供的介面不變,客戶端程式不會有絲毫的改動,不會帶來動一發而牽全身的災難, 這就是良好封裝性的體現.但如果直接用ConcreteProduct和ConcreteCreator兩個類是無論如何也做不到這點的. 優良的面向物件設計鼓勵使用封裝(encapsulation)和委託(delegation),而Factory Method模式就是使用了封裝和委託的典型例子,這裡封裝是通過抽象建立器Creator來體現的,而委託則是通過抽象建立器把建立物件的責任完全交給具體建立器ConcreteCreator來體現的.
該模式採用一個Shape(形狀)的經典例子作為一個例項來展示如何實現Factory Method模式,先看下程式碼的結構圖:
3.1 首先定義一個抽象類Shape,定義兩個抽象的方法.
package com.andyidea.patterns.product;
/**
* Product: 需要建立的產品的抽象類.
* @author Andy.Chen
*
*/
public abstract class Shape {
public String name;
public Shape(String aName){
this.name = aName;
}
//繪畫
public abstract void draw();
//擦除
public abstract void erase();
}
3.2 定義 Shape的兩個子類: Circle, Square,實現Shape中定義的抽象方法
Circle中的原始碼如下:
package com.andyidea.patterns.concreteproduct;
import com.andyidea.patterns.product.Shape;
/**
* 圓形子類(ConcreteProduct: Product的子類,一系列具體的產品.)
* @author Andy.Chen
*
*/
public class Circle extends Shape{
public Circle(String name) {
super(name);
}
@Override
public void draw() {
System.out.println("It will draw a Circle");
}
@Override
public void erase() {
System.out.println("It will erase a Circle");
}
}
Square中的原始碼:
package com.andyidea.patterns.concreteproduct;
import com.andyidea.patterns.product.Shape;
/**
* 方形子類(ConcreteProduct: Product的子類,一系列具體的產品.)
* @author Andy.Chen
*
*/
public class Square extends Shape{
public Square(String name) {
super(name);
}
@Override
public void draw() {
System.out.println("It will draw a Square");
}
@Override
public void erase() {
System.out.println("It will erase a Square");
}
}
3.3 定義抽象的建立器,anOperation呼叫factoryMethod建立一個物件,並對該物件進行一系列操作.
package com.andyidea.patterns.creator;
import com.andyidea.patterns.product.Shape;
/**
* Creator: 抽象建立器介面,宣告返回Product型別物件的Factory Method.
* @author Andy.Chen
*
*/
public abstract class ShapeFactory {
protected abstract Shape factoryMethod(String aName);
public void anOperation(String aName){
Shape s = factoryMethod(aName);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
3.4 定義與circle和square相對應的兩個具體建立器CircleFactory,SquareFactory,實現父類的methodFactory方法
CircleFactory中的原始碼:
package com.andyidea.patterns.concretecreator;
import com.andyidea.patterns.concreteproduct.Circle;
import com.andyidea.patterns.creator.ShapeFactory;
import com.andyidea.patterns.product.Shape;
/**
* ConcreteCreator: 具體的建立器,重寫Creator中的Factory Method,
* 返回ConcreteProduct型別的例項.
* @author Andy.Chen
*
*/
public class CircleFactory extends ShapeFactory {
@Override
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
SquareFactory中的原始碼:
package com.andyidea.patterns.concretecreator;
import com.andyidea.patterns.concreteproduct.Square;
import com.andyidea.patterns.creator.ShapeFactory;
import com.andyidea.patterns.product.Shape;
/**
* ConcreteCreator: 具體的建立器,重寫Creator中的Factory Method,
* 返回ConcreteProduct型別的例項.
* @author Andy.Chen
*
*/
public class SquareFactory extends ShapeFactory {
@Override
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
3.5 測試類MainClient:這個客戶端程式沒有羅嗦的條件判斷語句,也無需關心ConcreteProduct和ConcreteCreator的細節(因為這裡我用anOperation封裝了Product裡的兩個方法,所以連Product的影子也沒看見,當然把Product裡方法的具體呼叫放到客戶程式中也是不錯的).package com.andyidea.patterns.client;
import com.andyidea.patterns.concretecreator.CircleFactory;
import com.andyidea.patterns.concretecreator.SquareFactory;
import com.andyidea.patterns.creator.ShapeFactory;
/**
* 測試設計模式類
* @author Andy.Chen
*
*/
public class MainClient {
public static void main(String[] args) {
ShapeFactory sf1 = new CircleFactory();
ShapeFactory sf2 = new SquareFactory();
System.out.println("Welcome to Andy.Chen Blog!" +"\n"
+"Factory Method Patterns." +"\n"
+"-------------------------------");
sf1.anOperation("Shape-Circle");
sf2.anOperation("Shape-Square");
}
}
【4】程式執行結果如下:
Welcome to Andy.Chen Blog!
Factory Method Patterns.
-------------------------------
The current shape is: Shape-Circle (created by CircleFactory)
It will draw a Circle
It will erase a Circle
The current shape is: Shape-Square (created by SquareFactory)
It will draw a Square
It will erase a Square
【5】總結:用Factory Method模式建立物件並不一定會讓我們的程式碼更短,實事上往往更長,我們也使用了更多的類,真正的目的在於這樣可以靈活的,有彈性的建立不確定的物件.而且,程式碼的可重用性提高了,客戶端的應用簡化了,客戶程式的程式碼會大大減少,變的更具可讀性。