有些內容使用中英雙語,有些只有英文或中文。歡迎使用與分享任何內容,但先來信告知並標示此部落格為出處。
Some parts use both Chinese and English, but some parts use only one language. Feel free to share, but please contact me first and list this blog as your reference.

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!