1. 程式人生 > >Java設計模式百例 - 簡單工廠模式

Java設計模式百例 - 簡單工廠模式

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. ShapeFactorygetShape(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設計模式百例 - 簡單工廠模式