2014年5月17日 星期六

Design Patterns 筆記 - Simple Factory Pattern 簡單工廠

初學Design Pattern,如有謬誤或超級大錯還請指教。

如不知道什麼是 Design Patterns,可先閱覽一下這篇:Design Patterns 筆記 - Overview
想知道其他種 Design Patterns,請看這篇:Design Patterns 筆記 - Categories 分類條列

另外,如果不懂Java的介面,可以參考這一篇:JAVA - 什麼是介面?
===================================

前述:

以下內容主要從 tutorialspoint 的 Design Patterns - Factory Pattern 翻譯而來。
在網站上只有單純寫 Factory Pattern,但依王者歸來:品味Java的21種設計模式中,
此篇應該是 Simple Factory Pattern 簡單工廠,嚴格而言不是標準的 Design Pattern,
在GoF中的著作沒有。(非 Factory Method 也不是 Abstract Factory)

另外,tutorialspoint 居然沒有Factory Method....之後我應該會找其他資料,再寫一篇


概念 :

工廠模式 (Factory pattern) 是Java中最常使用的design pattern之一。
這個pattern是由建立型模式 (Creational Patterns)中而來,建立物件時最好的方式之一。

使用 Factory pattern ,我們不用暴露程式的創造邏輯(封裝化)就能讓使用者(客戶)創造物件。
並讓客戶用一個相同的介面來操作這些物件。


實作範例 :

我們先創造一個形狀(shape)的介面和一些實作此介面的類別。
再來定義一個工廠類別(ShapeFactory)。

運作的主程式(FactoryPatternDemo)會使用工廠類別(ShapeFactory)來取得形狀(shape)的物件。
主程式會傳遞資訊(CIRCLE 圓圈/ RECTANGLE 矩形 / SQUARE 正方型)給工廠類別(ShapeFactory),以取得它所需的物件。

請看下圖的UML圖。(如果看不懂,請查詢 class diagram。)


Factory Pattern UML Diagram


Step 1

創造一個介面(interface)。
有一個尚未被實作的函式 draw()。

Shape.java
public interface Shape {
   void draw();
}


Step 2

創造實作(implements)上述介面的三個類別。
實作draw(),輸出不同的訊息而已。

Rectangle.java
public class Rectangle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}
Square.java
public class Square implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}
Circle.java
public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}


Step 3

一個能依照所給訊息(shapeType)產生物件的工廠。
並會回傳不同的物件形式。

ShapeFactory.java
public class ShapeFactory {
 
   //use getShape method to get object of type shape 
   public 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("SQUARE")){
         return new Square();
      }
      return null;
   }
}


Step 4

這是主程式(main function)。
傳遞資訊給 Factory,得到想要的實際物件。

FactoryPatternDemo.java
public class FactoryPatternDemo {

   public static void main(String[] args) {
      ShapeFactory shapeFactory = new ShapeFactory();

      //get an object of Circle and call its draw method.
      Shape shape1 = shapeFactory.getShape("CIRCLE");

      //call draw method of Circle
      shape1.draw();

      //get an object of Rectangle and call its draw method.
      Shape shape2 = shapeFactory.getShape("RECTANGLE");

      //call draw method of Rectangle
      shape2.draw();

      //get an object of Square and call its draw method.
      Shape shape3 = shapeFactory.getShape("SQUARE");

      //call draw method of circle
      shape3.draw();
   }
}


Step 5

確認執行結果
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.


額外補充:靜態工廠 static Factory

根據書上講法,使用簡單工廠時,通常不用建立工廠類別的實例,沒有建立實例的必要。
把簡單工廠視為一個工具類別,直接用靜態方法(static)即可! (只有一個物件,不用再new() )

因為簡單工廠常為靜態的,也被稱為靜態工廠! 若要防止用戶隨意創造工廠物件,也能把工廠的constructor 設為 private。

如果是靜態工廠的話,程式碼應該會像這樣~ (請跟上面比較)

FactoryPatternDemo.java 裡面把 getShape 宣告為 private 和 static

ShapeFactory.java 裡面就不需要 ShapeFactory shapeFactory = new ShapeFactory();
應可直接使用shapeFactory.getShape

小結:

使用者只須跟"工廠 Factory "溝通,不必花心思了解複雜的原始程式,
只需理解工廠(ShapeFactory)的操作模式,就可以輕鬆使用這些物件。

而程式開發者可以將成是實作的部分包裝起來。
在 main function中,就算使用者完全看不到圓形、矩形、正方型類別,
或是看不到ShapeFactory.java 的內部程式碼,也不會造成用戶不能使用這些物件!
所以可以將這些程式碼封裝起來!


延伸閱讀:

1. Design Patterns 筆記 - Overview
2. Design Patterns 筆記 - Categories 分類條列 (其他Design Pattern 介紹)

Reference:

1. tutorialspoint
2. 王者歸來:品味Java的21種設計模式


If you want to use (copy, paste or quote) my original article, please contact me through email (autek.roy@gmail.com). If there is any mistake or comment, please let me know. :D

如要使用(複製貼上或轉載)作者原創文章,請來信跟我聯絡 (autek.roy@gmail.com)。如果有發現任何的錯誤與建議請留言或跟我連絡。 : )

沒有留言:

張貼留言

請留下您的任何想法或建議!
Please leave any thought or comment!