我們最常用 new
這個關鍵字, new 出一個物件, 並且操作這個物件的一些 action.
例如:
public class Saleman
{
public function doSomething()
{
var obj:Object = new Product();
obj.exe( );
}
}
Saleman 類別創建了一個 Product 類別的 instance 並且指派它給變數 obj.
注意: 什麼是組合? 什麼是繼承?
這段程式碼看起來沒什麼錯誤, 但這這裡 Saleman 和 Product 是有關連性的, 也就是說, Saleman 有 has
Product 的關連. 而且, 當我們呼叫 Saleman 的 doSomething(), 它其實又使用到了 Product 的 exe().
簡單地說, Saleman 類別相依了 Product 類別的方法.
這意謂了什麼? 這意謂著假始有很多個 Saleman 類別使用著 Product 類別, 那麼到時你需要更改程式碼的地方就很多.
目前 Saleman 和 Prodcut 的關係是屬於緊耦合的, 我們必須把它鬆散化. 工廠模式提供了一個 solution, 在 Saleman 和 Product 之間會有一個中介者 creator
, 它充許 Saleman 存取 obj 不用準確地指定 obj 的類別. 藉由 factory creator 中一個獨立的 method 來委派 obj. 工廠模式最原始的目的就是 new 出一個物件,並且回傳它.
我們先來寫 creator class, 而它會 create 和 return 一個 Product object.
public class Creator
{
public static function simpleFactory(product:String){
if (product == "p1"){
return new Product1();
}
else if (product == "p2") {
return new Product2();
}
}
}
這裡有個參數化方法稱作 simpleFactory(), 它能把 Product 類別實體並且回傳. 我們藉由參數化傳值(p1, p2), 來達成鬆散化. 但是, 如果我們再增加一個新的 Prodcut3 呢? 是不是再多一個 else if 分支下去. 這違反了開閉原則 - 方法(或者類別)應該對拓展開放, 對修改關閉.
讓我們來看 Class diagram of the factory method pattern
抽象類別是無法被實例化的, 它就是等著別人來繼承它的. 它們能有抽象方法, 或者開方法的規格出來, 請子類別到時再去實作它.
不幸的是, ActionScript 3.0 並沒有 抽象類別這種東西.
首先, 先定義產品介面 IProduct.as
package factory_pattern
{
public interface IProduct
{
function manipulate():void;
}
}
Product1.as
package factory_pattern
{
internal class Product1 implements IProduct
{
public function manipulate():void
{
trace("Doing stuff with Product1");
}
}
}
Product2.as
package factory_pattern
{
internal class Product2 implements IProduct
{
public function manipulate():void
{
trace("Doing stuff with Product2");
}
}
}
這裡類別存取修飾字我們使用 internal
( ActionScript 3.0 預設的類別存取修飾字 ), 這指的是並不是所有地方都能看到這個類別, internal 代表的只有同一個 package 底下的才看的到這個類別, 在這裡, 也就是 package factory_pattern 底下的.
所有的創建者類別一樣屬於 package factory_pattern 底下, 但是類別存取修飾字我們會使用 public
, 也就是它能被別的 package 所存取.
Creator.as
package factory_pattern
{
import flash.errors.IllegalOperationError;
// # 抽象類別,等著被繼承,而且不能被實體化
public class Creator
{
public function doStuff( ):void
{
var product:IProduct = this.factoryMethod();
product.manipulate();
}
// # 抽象方法,等著子類別來實作它
protected function factoryMethod( ):IProduct
{
throw new IllegalOperationError("Abstract method: must be overridden in a subclass");
return null;
}
}
}
注意, 在Creatro.as 中 的 doStuff() 是宣告成 public 是因為我們允許使用者能從外部的 package 可見它. 相反地, factoryMethod() 是宣告成 protected 是迫使方法只能在繼承體系下才能可見這個方法.
再來我們創建 CreatorA 和 CreatorB 類別繼承 Creator
CreatorA.as
package factory_pattern
{
public class CreatorA extends Creator
{
public function CreatorA()
{
super();
}
override protected function factoryMethod( ):IProduct
{
trace("Creating product 1");
return new Product1( ); // returns concrete product
}
}
}
CreatorB.as
package factory_pattern
{
public class CreatorB extends Creator
{
override protected function factoryMethod( ):IProduct
{
trace("Creating product 2");
return new Product2(); // returns concrete product
}
}
}
現在寫一個程式入口點來測試看看 main.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="initComp(event)">
<fx:Script>
<![CDATA[
import factory_pattern.Creator;
import factory_pattern.CreatorA;
import factory_pattern.CreatorB;
import mx.events.FlexEvent;
private function initComp(event:FlexEvent):void
{
// instantiate concrete creators
var cA:Creator = new CreatorA();
var cB:Creator = new CreatorB();
// creators operate on different products
// even though they are doing the same operation
cA.doStuff();
cB.doStuff();
}
]]>
</fx:Script>
</s:WindowedApplication>
在 main 中, 根本不知道任何關於 Product 類別, 它只知道了 Creator 類別以及它們做了什麼. 在 main 中實例化了 CreatorA 和 CreatorB, 然後要求它們 doStuff(). 在 Creator 為抽象類別時, 它就有一個 public 的方法叫做 doStuff(), 它允許子類別來決定應該去處理哪個產品.
在 Creator 中, 有一個 method 叫 doStuff(), doStuff() 呼叫 factoryMethod() 來回傳一個 Product object. 然後這個 Product object 呼叫 manipulate() 方法.
或許你會想我們為什麼如此麻煩來直接實例化一個類別. 在這裡, Product1 和 Product2 是直接被封裝起來了, 在 main 我們只直接知道 CreatorA 和 CreatorB 的存在.
我們開發一個小型的應用程式來模擬. 想像一下有個研究生帶了 usb 裡面存了論文以及個人簡歷想列印. 論文有 200 多頁, 圖文並茂, 而個人簡歷只有 3 頁. 櫃台人員熟悉店裡的列印設備和操作流程. 他會知道論文該指派哪台機器印會比較快速(例如: 裝訂, 封膠)或者輸出比較好. 我們將使用這樣的情境來講述工廠模式的例子.
我們需要的類別將會有:
在這裡, 我們的 Print Job 先簡單的列印文件
public interface IPrintjob
{
function start(fileName:String):void;
}