了解模式需要研究客戶端程序(使用模式者)和模式的內部結構
並且理解兩者之間的通信接口
模式本身因功能增加造成的變動(易碎性)
以及客戶端程序利用新功能的難易程度
本文針對這些問題提出了一個具體的抽象工廠模式實現方案
模式結構介紹
這個模式由一個工廠類層次和N個產品類層次組成
從每一個產品類層次中取出一個產品類形成產品類族
這個類族的實例為產品族
產品族中的產品之間有一種依賴關系
一個具體的工廠類負責創建產品族中的各個產品
從圖
可以看出
通信接口由一個抽象工廠接口和兩個抽象產品組成
模式部分顯示了兩個產品類 ProductA 和ProductB
兩個產品類族ProductA
ProductB
和ProductA
ProductB
以及兩個產品類族對應的兩個工廠ConcreteFactory
ConcreteFactory
圖
標准抽象工廠模式
從模式定義中知道這個模式的意圖內容為
提供一個創建一系列相關或相互依賴對象的接口
而無需指定它們具體的類
工廠類層次的通信接口只有抽象工廠和創建產品族的各個工廠方法
這些工廠方法不帶任何參數
並且返回具有抽象產品類型的具體產品實例
這些使得客戶端可以不依賴具體產品的類
從而體現了模式的意圖
意圖中的
而無需指定它們具體的類
可以理解為客戶端在使用和創建具體產品時不給出具體產品的任何暗示
變動
我們分析模式的變動時必須先固化模式和客戶端間的通信接口
即通信接口是固定不變的
分析變動就是分析模式的易碎性(模式實現適應變動的能力)
對抽象工廠模式可以考慮兩個變動
加一個產品類族
加一個產品類
變動一的易碎性較小
我們只需在每個產品類層次中增加這個產品族中產品類作為抽象產品類的子類
同時增加一個工廠子類來創建這個產品類族的產品族足以
變動二要求在模式中增加一個產品類
意味著增加一個產品類層次
由於通信接口中的工廠方法固定了客戶能創建的產品的類型數目
所以增加一個產品類意味著修改與客戶端的通信接口
進一步意味著舊的客戶端要使用新的模式實現就要重新編碼
整個工廠類層次都需要改動
綜合上述
這個變動的易碎性很大
設計
程序架構
一個程序可以從三個維度進行架構
這三個維度是層
級和服務
層代表了不同的抽象程度
比如操作系統的分層
級代表不同的角色和職責
比如Client/Server的兩級模型
服務代表了具有通用功能的支持模塊
實現一個模式從級這個維度來講可以簡單地分成兩級
即客戶端和模式端
從服務維度講模式可以利用某些服務
如事務
名字和目錄服務
安全等服務器提供商開發的設施服務
模式本身也可以實現成供客戶端訪問的服務
從而形成應用服務
典型的有EJB組件
應用程序的開發從層上考慮的比較少
圖
體現了實現抽象工廠模式的程序架構
圖
程序架構
JNDI
JNDI是Java Naming and Directory Interface的簡寫
即Java命名和目錄服務接口
這個接口是名字和目錄服務通用編程的API
命名服務是一個系統基礎設施
給對象綁定一個名字
並能通過名字找到對象的機制
這個名字一般是面向使用者的
類似的服務實現有因特網域名系統(實現通過域名www
找到IP地址的手段)
文件系統(實現通過文件名找到文件的手段)等
目錄服務是名字服務的擴展
除了提供名字綁定之外
還允許對象擁有屬性
目錄服務中的對象為目錄對象
圖
和圖
分別表示了名字服務和目錄服務的概念定義
圖
名字服務的概念
圖
目錄服務的概念
一個名字系統由相互聯系的一系列上下文組成
上下文是名字與對象的綁定集合
相互聯系的一組上下文能形成一個層次結構
一個名字系統中所有的名字組成了這個系統的命名空間
而且命名空間有它自己的命名規范
一個目錄庫由一系列目錄對象組成
每個目錄對象可以有若干個屬性相連
JNDI架構如圖
其中JNDI SPI為服務提供者接口
LDAP
DNS
NIS等為服務提供者
JNDI API為客戶程序使用服務的編程接口
圖
JNDI架構
JNDI Java包
JDK
和後續版本已經包含了JNDI
另外還有幾個服務提供者
如LDAP
COS和RMI
其他的服務提供者可以從下載
JNDI分為五個包javax
naming
javax
naming
directory
javax
naming
event
javax
naming
ldap 和javax
naming
spi
一般情況下只需要前三個包
名字和目錄操作都是針對一個上下文來說的
但是沒有絕對根的上下文
所以就用一個初始上下文 InitialContext作為名字和目錄操作的起點
一旦有了這個上下文
就可以用它來查找其它上下文和對象
下面是進行名字操作和目錄操作初始上下文類的關系
使用JNDI
在模式的實現中
我們可以用名字服務來實現子類的配置
也就是說在名字服務中指定子類的名字
由模式讀取這個子類配置
並創建一個子類實例返回給客戶端
客戶端用抽象父類來返回子類實例
通過這種機制實現
針對接口編程
而不是實現
的重用的面向對象設計的原則
Singlton模式的實現可以利用JNDI名字服務來實現
在名字服務中存放Singlton模式中的惟一實例
使用者要使用這個實例可以使用JNDI編程接口查詢這個對象
我們的工廠類對象的產生就可以采用這個方法
類圖設計
目前沒有較好的辦法解決增加產品類帶來的舊客戶程序不能透明地使用新產品類的方案
如果能忍受這一點
這個變動的其它影響還是可以解決的
圖
為一個基於Class第一類對象的Java抽象工廠的模式變體結構圖
由此可以看出
抽象工廠不僅是接口
還是具體完成創建工作的類
它只有一個工廠方法
以抽象產品的類名為參數
以Java類庫的最頂層類Object為返回值
客戶端把通信接口抽象產品類名傳給這個工廠方法
接著通過一個強制類型轉換而得到抽象產品對象
命名服務實現產品類族中每個產品類名到具體產品類名的映射
通過這樣一系列的映射定義了每一個具體工廠類要創建的產品類族
圖
抽象工廠模式變體類結構圖
圖
中還顯示了編寫單元測試用例的類UnitTest和實用類SerObj
實現工廠通信接口的設計
.客戶端代碼如下
Factory factory = Factory
getInstance
(
ldap://localhost:
/ dc = pattern
dc = com
) ;
AbstractProductA productA = factory
CreateProduct
( AbstractProductA的完全類名 );
名字服務包含Factory的名字到工廠類對象的綁定
整個機制如圖
圖
工廠類的Singleton設計
.單元測試代碼如下
.實現工廠Singlton模式的代碼如下:
.具體的工廠方法代碼如下
public class Factory implements java
io
Serializable {
/**
接受抽象產品類的完全類名
查詢目錄服務得到具體產品類的完全類名
采用JAVA的CLASS為第一類對象的機制創建相應類的對象
*/
public Object createProduct(String vstrClassName ){
Context ctx = null;
String strEntryName;
String strEntryClassName;
Object oResult = null;
try{
ctx = getInitialContext ( ) ;
strEntryName =
cn=
+ vstrClassName;
strEntryClassName = ( String )ctx
lookup ( strEntryName ) ;
try {
Class clTem;
clTem = Class
forName ( strEntryClassName ) ;
oResult = clTem
newInstance ( ) ;
}
catch ( Exception ex ) {
logger
error ( ex
toString ( ) );
}
}
catch ( NamingException e ) { }
finally{
try{
ctx
close ( ) ;
}
catch ( Exception e
) { }
}
return oResult;
}
}
實現產品通信接口的設計
抽象工廠模式的本意要求我們創建具體產品對象時
客戶端不能暗示任何具體產品對象類型的信息
但是通過分析模式的通信接口可知
客戶端可以告訴工廠類這些具體產品的父類
關於模式提到創建一些互相依賴的對象的本意
我們可以在目錄服務的目錄庫中實現
圖
圖
分別定義了這樣的產品族關系
圖
目錄服務產品族
的配置
圖
目錄服務產品族
的配置
.圖
表示在工廠類的工廠方法要求創建產品族
時
可以在目錄服務中指定抽象產品的完全類名綁定到產品族
的相應類名上
(
)單元測試代碼如下
public class UnitTest extends TestCase{
public void testProduct
( ) {
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27592.html