這個模式的定義:
定義一個用於創建物件的介面, 讓子類別決定要實例化哪一個類別. 工廠方法讓一個類別的實例化遞延到其子類別.
Define an interface for creating an object, but let subclasses decide wihich class to instantiate. Factory Method lets a class defer instantiation to subclasses.
從上段語意摘要出的兩個重點:
1. 創建物件的介面
2. 讓一個類別的實例化遞延到其子類別
工廠方法非常有用也常常被實用, 來看看下圖所示:
工廠的目的是用來產生產品, 產品則是服務客戶. Creator通常是一個抽象, 他可以對相關的具象類別產生約束力. 來看看下面的代碼範例:
這是一個非常簡單的範例, 一個IDbConnectionFactory抽象包含了CreateConnection方法的簽章, 這個方法會對其相關的具象類別產生約束性, 良好的設計可以提高內聚力. 從範例可以非常清楚的看出相關的具象類別用途只用來產出DbConnection的資料庫連線物件, 並且返回.
對應到第一張的UML圖, factoryMethod的操作限制了ConcreteCreator的使用. 還有一個重點就是, 可以看出真正的實例化並不是在抽象發生, 抽象不允許相關的實作, 但是我們可以利用具象類別對抽象進行實作. MsSqlDbConnectionFactory正是對IDbConnectionFactory的CreateDbConnection方法進行了實作. 所以真正的產品產生會遞延到相關具象子類別.
透過工廠方法可以產生各種不同的工廠, 這些工廠用以產生不同的產品. 根據前面的範例, 我想擴充一個OLEDB用的連線物件, 只要另外產生出專門用於OLEDB連線物件的工廠即可, 如下代碼範例所示:
可以看出同一用途的工廠, 但是結果卻是不一樣, 一個是用來產生MS SQL的連線; 另一個則是OLEDB的連線, 彼此完全不相關. 這意味著在良好的設計下, 工廠商法可以產生絕佳的擴展性.
在Client實際的應用下, Client只需要知道有工廠方法對應的簽章存在就好, 其細節並不是重點, 也並不需要知道. 這完全封裝(隱藏)了真正的運作過程, 降低了耦合. 來看看如下的代碼範例:
我在Page_Load的樣板方法調用了上面範例的連線工廠物件, Client端的Page_Load並不需要CreateDbConnection方法的細節為何. 他僅需要知道有這個方法存在並且可以使用即可.
實務案例: ADO.NET相關物件(ADO.NET的資料庫連線物件)
在ADO.NET中, 可以使用Connection物件進行對database的連線通訊, 來看看他的類別結構, 如下圖所示:
從上圖可以看到關鍵性的DbConnection, 他是一個抽象. 他分別繼承了一個類別和兩個介面, 可以關注到一個上層的介面: IDbConnection. 這個介面的抽象主要提供基礎的存取屬性和方法以供次類別實作. IDbConnection會對其下的子類別產生一定程度的約束力, DbConnection則是對這個抽象的豐富化, 這個豐富化的抽象提供更多的存取屬性和方法, 目前微軟於ADO.NET提供了幾種對DbConnection的實作, 從上圖來看共有五種. 對泛生類別來說, DbConnection是可以提供創建物件的一個介面, 其相關的實作會遞延到具象類別實現. 例如來看看SqlConnection的具象類別.
SqlConnection是對DbConnection抽象的實作, 主要用來提供對MSSQL的連線操作.來關注在SqlConnection的兩個方法操作:
我用Reflector拆解了System.Data.dll組件的內容, 關注在創建Command物件的兩個方法: CreateCommand和CreateDbCommand. 其中CreateCommand不是來自對抽象的實作, 他是一個擴充, 而CreateDbCommand則是對抽象的實作. 這裡可以看到一個編程上的技巧, CreateDbCommand正是工廠的Command產生方法, 可是他是protected的, 不公開外界存取. 所以在DbConnection的抽象上提供了CreateCommand一般方法, 讓屬於DbConnection的物件使用CreateCommand存取CreateDbCommand實作方法, 並且會返回對應的DbCommand物件.
從上圖看到了這個作法, CreateCommand是間接產生Command物件的工廠方法, 他是另外呼叫一個不被公開的CreateDbCommand方法, 但是這個方法實際的產生過程則是被遞延到泛生類別實作, 提供對應的實例化.
上圖為SqlConnection的具象類別對DbConnection的CreateDbCommand抽象方法進行實作, 細節不必在乎, 只要知道這個方法產生出來的物件是用來對MSSQL進行連線存取的用途即可. 這個案例正是工廠方法的一種應用, 兩個重點如下:
1. 用以提供創建產品的一種介面=> DbConnection的CreateDbCommand
2. 真正的創建過程遞延到泛生類別的實作方法=> SqlConnection的CreateDbCommand
文件 | 大小 | 日期 | 附件上傳者 | |||
---|---|---|---|---|---|---|
201203221601_dp_factorymethod.gif 無描述 | 3.89 KB | 17:29, 22 Mar 2012 | vxr | 動作 | ||
201203221619_dp_factorymethod.gif 無描述 | 3.05 KB | 17:19, 22 Mar 2012 | vxr | 動作 | ||
201203221633_dp_factorymethod.gif 無描述 | 3.09 KB | 17:33, 22 Mar 2012 | vxr | 動作 | ||
201203221643_dp_factorymethod.gif 無描述 | 11.76 KB | 17:44, 22 Mar 2012 | vxr | 動作 | ||
201203230852_dp_factorymethod.gif 無描述 | 9.16 KB | 08:53, 23 Mar 2012 | vxr | 動作 | ||
201203230926_dp_factorymethod.gif 無描述 | 8 KB | 09:26, 23 Mar 2012 | vxr | 動作 | ||
201203230937_dp_factorymethod.gif 無描述 | 13.39 KB | 09:38, 23 Mar 2012 | vxr | 動作 | ||
201203230946_dp_factorymethod.gif 無描述 | 7.23 KB | 09:46, 23 Mar 2012 | vxr | 動作 | ||
500px-FactoryMethod.svg.png 無描述 | 10.87 KB | 16:52, 22 Mar 2012 | vxr | 動作 |