在設(shè)計模式中,Factory Method也是比較簡單的一個,但應(yīng)用非常廣泛,EJB,RMI,COM,CORBA,Swing中都可以看到此模式的影子,它是最重要的模式之一.在很多地方我們都會看到xxxFactory這樣命名的類,那么,什么是Factory Method,為什么要用這個模式,如何用java語言來實現(xiàn)該模式,這就是本文想要帶給大家的內(nèi)容.
基本概念
Factory Method是一種創(chuàng)建性模式,它定義了一個創(chuàng)建對象的接口,但是卻讓子類來決定具體實例化哪一個類.當一個類無法預(yù)料要創(chuàng)建哪種類的對象或是一個類需要由子類來指定創(chuàng)建的對象時我們就需要用到Factory Method 模式了.簡單說來,Factory Method可以根據(jù)不同的條件產(chǎn)生不同的實例,當然這些不同的實例通常是屬于相同的類型,具有共同的父類.Factory Method把創(chuàng)建這些實例的具體過程封裝起來了,簡化了客戶端的應(yīng)用,也改善了程序的擴展性,使得將來可以做最小的改動就可以加入新的待創(chuàng)建的類. 通常我們將Factory Method作為一種標準的創(chuàng)建對象的方法,當發(fā)現(xiàn)需要更多的靈活性的時候,就開始考慮向其它創(chuàng)建型模式轉(zhuǎn)化
簡單分析
圖1是Factory Method 模式的結(jié)構(gòu)圖,這里提供了一些術(shù)語,讓我們可以進行更方便的描述:
PRodUCt: 需要創(chuàng)建的產(chǎn)品的抽象類.
ConcreteProduct: Product的子類,一系列具體的產(chǎn)品.
Creator: 抽象創(chuàng)建器接口,聲明返回Product類型對象的Factory Method.
ConcreteCreator: 具體的創(chuàng)建器,重寫Creator中的Factory Method,返回ConcreteProduct類型的實例.
圖1: Factory Method 模式結(jié)構(gòu)
由此可以清楚的看出這樣的平行對應(yīng)關(guān)系: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象產(chǎn)品對應(yīng)抽象創(chuàng)建器,具體產(chǎn)品對應(yīng)具體創(chuàng)建器.這樣做的好處是什么呢?為什么我們不直接用具體的產(chǎn)品和具體的創(chuàng)建器完成需求呢?實際上我們也可以這樣做.但通過Factory Method模式來完成,客戶(client)只需引用抽象的Product和Creater,對具體的ConcreteProduct和 ConcreteCreator可以毫不關(guān)心,這樣做我們可以獲得額外的好處:
首先客戶端可以統(tǒng)一從抽象創(chuàng)建器獲取產(chǎn)生的實例, Creator的作用將client和產(chǎn)品創(chuàng)建過程分離開來,客戶不用操心返回的是那一個具體的產(chǎn)品,也不用關(guān)心這些產(chǎn)品是如何創(chuàng)建的.同時, ConcreteProduct也被隱藏在Product后面,ConreteProduct繼續(xù)了Product的所有屬性,并實現(xiàn)了Product中定義的抽象方法,按照Java中的對象造型(cast)原則,通過ConcreteCreator產(chǎn)生的ConcreteProduct可以自動的上溯造型成Product.這樣一來,實質(zhì)內(nèi)容不同的ConcreteProduct就可以在形式上統(tǒng)一為Product,通過Creator提供給 client來訪問.
其次,當我們添加一個新的ConcreteCreator時,由于Creator所提供的接口不變,客戶端程序不會有絲毫的改動,不會帶來動一發(fā)而牽全身的災(zāi)難, 這就是良好封裝性的體現(xiàn).但假如直接用ConcreteProduct和ConcreteCreator兩個類是無論如何也做不到這點的. 優(yōu)良的面向?qū)ο笤O(shè)計鼓勵使用封裝(encapsulation)和委托(delegation),而Factory Method模式就是使用了封裝和委托的典型例子,這里封裝是通過抽象創(chuàng)建器Creator來體現(xiàn)的,而委托則是通過抽象創(chuàng)建器把創(chuàng)建對象的責任完全交給具體創(chuàng)建器ConcreteCreator來體現(xiàn)的.
現(xiàn)在,請再回頭看看基本概念中的那段話,開始也許覺得生澀難懂,現(xiàn)在是不是已經(jīng)明朗化了很多.
下面讓我們看看在 Java 中如何實現(xiàn)Factory Method模式,進一步加深對它的熟悉.
具體實施
先說明一點,用Factory Method模式創(chuàng)建對象并不一定會讓我們的代碼更短,實事上往往更長,我們也使用了更多的類,真正的目的在于這樣可以靈活的,有彈性的創(chuàng)建不確定的對象.而且,代碼的可重用性提高了,客戶端的應(yīng)用簡化了,客戶程序的代碼會大大減少,變的更具可讀性.
標準實現(xiàn): 這里我采用Bruce Eckel 用來描述OO思想的經(jīng)典例子 Shape.這樣大家會比較熟悉一些.我完全按照圖1中所定義的結(jié)構(gòu)寫了下面的一段演示代碼.這段代碼的作用是創(chuàng)建不同的Shape實例,每個實例完成兩個操作:draw和erase.具體的創(chuàng)建過程委托給ShapeFactory來完成.
1.a 首先定義一個抽象類Shape,定義兩個抽象的方法.
abstract class Shape {
// 勾畫shape
public abstract void draw();
// 擦去 shape
public abstract void erase();
public String name;
public Shape(String aName){
name = aName;
}
}
1.b 定義 Shape的兩個子類: Circle, Square,實現(xiàn)Shape中定義的抽象方法
// 圓形子類
class Circle extends Shape {
public void draw() {
System.out.println("It will draw a circle.");
}
public void erase() {
System.out.println("It will erase a circle.");
}
// 構(gòu)造函數(shù)
public Circle(String aName){
super(aName);
}
}
// 方形子類
class Square extends Shape {
public void draw() {
System.out.println("It will draw a square.");
}
public void erase() {
System.out.println("It will erase a square.");
}
// 構(gòu)造函數(shù)
public Square(String aName){
super(aName);
}
}
1.c 定義抽象的創(chuàng)建器,anOperation調(diào)用factoryMethod創(chuàng)建一個對象,并對該對象進行一系列操作.
abstract class ShapeFactory {
protected abstract Shape factoryMethod(String aName);
// 在anOperation中定義Shape的一系列行為
public void anOperation(String aName){
Shape s = factoryMethod(aName);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
1.d 定義與circle和square相對應(yīng)的兩個具體創(chuàng)建器CircleFactory,SquareFactory,實現(xiàn)父類的methodFactory方法
// 定義返回 circle 實例的 CircleFactory
class CircleFactory extends ShapeFactory {
// 重載factoryMethod方法,返回Circle對象
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
// 定義返回 Square 實例的 SquareFactory
class SquareFactory extends ShapeFactory {
// 重載factoryMethod方法,返回Square對象
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
1.e 測試類:請注重這個客戶端程序多么簡潔,既沒有羅嗦的條件判定語句,也無需關(guān)心ConcreteProduct和ConcreteCreator的細節(jié) (因為這里我用anOperation封裝了Product里的兩個方法,所以連Product的影子也沒看見,當然把Product里方法的具體調(diào)用放到客戶程序中也是不錯的).
class Main {
public static void main(String[] args){
ShapeFactory sf1 = new SquareFactory();
ShapeFactory sf2 = new CircleFactory();
sf1.anOperation("Shape one");
sf2.anOperation("Shape two");
}
}
運行結(jié)果如下:
The current shape is: Shape one (created by SquareFactory)
It will draw a square.
It will erase a square.
The current shape is: Shape two (created by CircleFactory)
It will draw a circle.
It will erase a circle.
參數(shù)化的Factory Method: 這種方式依靠指定的參數(shù)作為標志來創(chuàng)建對應(yīng)的實例,這是很常見的一種辦法.比如JFC中的BorderFactory就是個很不錯的例子. 以下的這個例子是用字符串作為標記來進行判定的,假如參數(shù)的類型也不一樣,那就可以用到過載函數(shù)來解決這個問題,定義一系列參數(shù)和方法體不同的同名函數(shù), 這里java.util.Calendar.getInstance()又是個極好的例子.參數(shù)化的創(chuàng)建方式克服了Factory Method模式一個最顯著的缺陷,就是當具體產(chǎn)品比較多時,我們不得不也建立一系列與之對應(yīng)的具體構(gòu)造器. 但是在客戶端我們必須指定參數(shù)來決定要創(chuàng)建哪一個類.
2.a 我們在第一種方法的基礎(chǔ)上進行修改,首先自定義一個的異常,這樣當傳入不正確的參數(shù)時可以得到更明顯的報錯信息.
class NoThisShape extends Exception {
public NoThisShape(String aName) {
super(aName);
}
}
2.b去掉了ShapeFactory的兩個子類,改為由ShapeFactory直接負責實例的創(chuàng)建. ShapeFactory自己變成一個具體的創(chuàng)建器,直接用參數(shù)化的方法實現(xiàn)factoryMethod返回多種對象.
abstract class ShapeFactory {
private static Shape s;
private ShapeFactory() {}
static Shape factoryMethod(String aName, String aType) throws NoThisShape{
if (aType.compareTo("square")==0)
return new Square(aName);
&nbs