Java設計模式百例 - 簡單工廠模式
工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象很好的方式。具體來說,有簡單工廠模式(simple factory)、工廠方法模式(factory method)和抽象工廠模式(abstract factory)等模式。
本篇先從簡單工廠模式談起。
例子
請設想一個簡單的需求,你在做一個畫圖軟件,可以畫矩形、三角形和圓形等,每一種圖形都用一個類來管理:
* Rectangle
* Circle
* Triangle
每個類都有各自的draw()
方法,當需要畫某種圖形的時候就可以:
Client.java
// 畫矩形時: Rectangle r = new Rectangle(); r.draw(); // 畫圓形時: Circle c = new Circle(); c.draw(); // 畫三角形時: Triangle t = new Triangle(); t.draw();
如果這時增加一個Star
類型的圖形,就得需要增加一個new Star()
來獲取對象,這顯然是不符合開閉原則。所謂開閉原則,即對擴展開放,對修改關閉。
要想實現開閉原則,就需要考慮把創建對象的過程統一起來,並且能夠滿足擴展圖形類型的需要。我們自然而然地就能想到這些圖形都是有共同點可以抽象的,它們都實現了draw()
方法,因此可以增加一個共同的接口,即“針對接口編程,依賴於抽象而不依賴於具體”,這就是“依賴倒轉”的原則。
Shape.java
public interface Shape { void draw(); }
Rectangle.java
public class Rectangle implements Shape { @Override public void draw() { System.out.println("Draw a rectangle."); } }
Triangle.java
public class Triangle implements Shape { @Override public void draw() { System.out.println("Draw a triangle."); } }
Circle.java
public class Circle implements Shape { @Override public void draw() { System.out.println("Draw a circle."); } }
然後屏蔽掉具體類型的創建語句,那麽就可以一定程度上做到“開閉原則”了。這時就需要工廠類登場了,它有一個用來返回具體產品對象的方法,註意這個方法是靜態(static)的,也因此簡單工廠模式有時候也被叫做“靜態工廠模式”。
ShapeFactory.java
public class ShapeFactory { //使用 getShape 方法獲取形狀類型的對象 public static Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); } else if(shapeType.equalsIgnoreCase("TRIANGLE")){ return new Triangle(); } return null; } }
有了工廠類,再畫圖的時候剛才的Client.java
就可以改一改了:
Client.java
//畫矩形時: Shape r = ShapeFactory.getShape("RECTANGLE"); r.draw(); //畫圓形時: Shape c = ShapeFactory.getShape("CIRCLE"); c.draw(); //畫三角形時: Shape t = ShapeFactory.getShape("TRIANGLE"); t.draw();
此時如果增加一個Star的圖形類型,也可以從容應對了,從使用者Client
的角度,無需關心對象是如何創建的。
各個類之間的關系如下:
此外還有一個類似的例子,可自行查看源碼:
看到這裏可能你會覺得,前後兩個版本的Client
並沒有太大的不同,反而采用了工廠模式後更加復雜了。其實作為一種創建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。有一點需要註意的地方就是復雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。
此外,在具體使用時,也會有不同的調整,比如:
1. ShapeFactory
的getShape(String)
方法也可以改為非static
的,這樣就需要首先構造一個實際的工廠對象來創建產品了;
2. 工廠類、抽象產品和具體產品這三個角色有可能部分或全部合並,我們來看一個Java中經常用到的關於日期類型格式化的例子:
import java.util.*; import java.text.*; class DateTest { public static void main(String[] args) { Locale local = Locale.FRENCH; Date date = new Date(); String now = DateFormat.getTimeInstance(DateFormat.DEFAULT,local).format(date); System.out.println(now); try { date = DateFormat.getDateInstance(DateFormat.DEFAULT,local).parse("16 nov. 01"); System.out.println(date); } catch (ParseException e) { System.out.println("Parsing exception:" + e); } } }
而我們知道DateFormat是個抽象類,public final static DateFormat getDateInstance(...)
的靜態方法,也使得它自身既是一個抽象產品角色,又是一個工廠類角色,完成具體產品角色的創建(比如SimpleDateFormat
)。
如果三個角色全部合並,其實就是一個類,增加了一個創建自身對象的方法。
總結
在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。
使用場景: 1、日誌記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶可以選擇記錄日誌到什麽地方。 2、數據庫訪問,當用戶不知道最後系統采用哪一類數據庫,以及數據庫可能有變化時。 3、設計一個連接服務器的框架,需要三個協議,”POP3”、”IMAP”、”HTTP”,可以把這三個作為產品類,共同實現一個接口。
優點: 一個調用者想創建一個對象,只要提出要求就可以了;屏蔽產品的具體實現,調用者只關心產品的接口;一定程度上提高了擴展性。
缺點:每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴。可見,簡單工廠模式對”開-閉”原則的支持還不夠,因為如果有新的產品加入到系統中去,就需要修改工廠類,將必要的邏輯加入到工廠類中。
Java設計模式百例 - 簡單工廠模式