一. 程式設計目標
我們組寫了個簡單的水果生產程式,描述農場種植水果的過程,旨在通過此次設計更進一步瞭解工程設計模式,加強編程的結構化能力。
開發環境:JDK1.5
開發工具:JBuilder 2006
二.程式設計介紹
1.程式結構
我們組為一個水果公司寫了個簡單的生產程式,該公司專門向市場銷售各類水果。我們為程式建立了一個名為farm的工程,程式結構比較簡單,總共有7個類,並且都放在一個預設的包中。其階層可從體現出來:
對各個類的說明:
Fruit類:水果介面,實現水果方法
Apple類:蘋果類,實現Fruit介面
Grape類:葡萄類,實現Fruit介面
Strawberry類:草莓類,實現Fruit介面
FruitGardener類:園丁類,可種植各種水果
BadFruitException類:要種植的水果不在公司經營的水果範圍之內,拋出種植異常
PlantFruit類:實現main()方法
2.程式設計步驟
在這個系統裡需要描述下列的水果:
葡萄 Grape
草莓 Strawberry
蘋果 Apple
水果與其他的植物有很大的不同,就是水果最終是可以採摘食用的。那麼一個自然的
作法就是建立一個各種水果都適用的介面,以便與農場裡的其他植物區分開。如所示。
水果介面規定出所有的水果必須實現的介面,包括任何水果類必須具備的方法:種植plant(),生長grow()以及收穫harvest()。介面Fruit 的類圖如下所示。
這個水果介面的原始碼如下所示。
代碼清單1:介面Fruit 的原始碼
public interface Fruit {
// 生長
void grow();
//收穫
void harvest();
//種植
void plant();
}描述蘋果的Apple 類的原始碼的類圖如下所示。
Apple 類是水果類的一種,因此它實現了水果介面所聲明的所有方法。另外,由於蘋果是多年生植物,因此多出一個treeAge 性質,描述蘋果樹的樹齡。下面是這個蘋果類的原始碼。
代碼清單2:類Apple 的原始碼
public class Apple
implements Fruit {
private int treeAge;
//生長
public void grow() {
log("Apple is growing...");
}
// 收穫
public void harvest() {
log("Apple has been harvested.");
}
//種植
public void plant() {
log("Apple has been planted.");
}
// 輔助方法
public static void log(String msg) {
System.out.println(msg);
}
//樹齡的取值方法
public int getTreeAge() {
return treeAge;
}
// 樹齡的賦值方法
public void setTreeAge(int treeAge) {
this.treeAge = treeAge;
}
}
同樣,Grape 類是水果類的一種,也實現了Fruit 介面所聲明的所有的方法。但由於葡萄分有籽和無籽兩種,因此,比通常的水果多出一個seedless 性質,如所示。
葡萄類的原始碼如下所示。可以看出,Grape 類同樣實現了水果介面,從而是水果類型的一種子類型。
代碼清單3:類Grape 的原始碼
public class Grape
implements Fruit {
private boolean seedless;
//生長
public void grow() {
log("Grape is growing...");
}
//收穫
public void harvest() {
log("Grape has been harvested.");
}
//種植
public void plant() {
log("Grape has been planted.");
}
//輔助方法
public static void log(String msg) {
System.out.println(msg);
}
// 有無籽的取值方法
public boolean getSeedless() {
return seedless;
}
//有無籽的賦值方法
public void setSeedless(boolean seedless) {
this.seedless = seedless;
}
}
所示是Strawberry 類的類圖。
Strawberry 類實現了Fruit 介面,因此,也是水果類型的子類型,其原始碼如下所示。
代碼清單4:類Strawberry 的原始碼
public class Strawberry
implements Fruit {
//生長
public void grow() {
log("Strawberry is growing...");
}
//收穫
public void harvest() {
log("Strawberry has been harvested.");
}
//種植
public void plant() {
log("Strawberry has been planted.");
}
//輔助方法
public static void log(String msg) {
System.out.println(msg);
}
}
農場的園丁也是系統的一部分,自然要由一個合適的類來代表。這個類就FruitGardener 類,其結構由下面的類圖描述。
FruitGardener 類會根據用戶端的要求,建立出不同的水果對象,比如蘋果(Apple),葡萄(Grape)或草莓(Strawberry)的執行個體。而如果接到不合法的要求,FruitGardener 類會拋出BadFruitException 異常,如所示。
園丁類的原始碼如下所示。
代碼清單5:FruitGardener 類的原始碼
public class FruitGardener {
//靜態Factory 方法
public static Fruit factory(String which) throws BadFruitException {
if (which.equalsIgnoreCase("apple")) {
return new Apple();
}
else if (which.equalsIgnoreCase("strawberry")) {
return new Strawberry();
}
else if (which.equalsIgnoreCase("grape")) {
return new Grape();
}
else {
throw new BadFruitException("Bad fruit request");
}
}
}
可以看出,園丁類提供了一個靜態Factory 方法。在用戶端的調用下,這個方法建立用戶端所需要的水果對象。如果用戶端的請求是系統所不支援的,Factory 方法就會拋出一個BadFruitException 異常。這個異常類的原始碼如下所示。
代碼清單6:BadFruitException 類的原始碼
public class BadFruitException
extends Exception {
public BadFruitException(String msg) {
super(msg);
}
}
在使用時,用戶端只需調用FruitGardener 的靜態方法factory()即可。請見下面的示意性用戶端原始碼。
代碼清單7:實現種植即Main()的實現
public class PlantFruit {
public PlantFruit() {
}
public static void main(String[] args) {
PlantFruit plantfruit = new PlantFruit();
try {
//種植葡萄
FruitGardener.factory("grape").plant();
FruitGardener.factory("grape").grow();
FruitGardener.factory("grape").harvest();
System.out.println("==================================");
//種植蘋果
FruitGardener.factory("apple").plant();
FruitGardener.factory("apple").grow();
FruitGardener.factory("apple").harvest();
System.out.println("==================================");
//種植草莓
FruitGardener.factory("strawberry").plant();
FruitGardener.factory("strawberry").grow();
FruitGardener.factory("strawberry").harvest();
System.out.println("==================================");
}
catch (BadFruitException e) {
}
}
}
到此為止,我們的簡單程式已經設計完成,我們可以通過建立FruitGardener對象來完成水果的種植,無論你要種什麼,只需調用對象中的factory()方法。輸出結果如下:
三.簡單原廠模式的定義
簡單原廠模式是類的建立模式,又叫做靜態Factory 方法(Static Factory Method)模式。簡單原廠模式是由一個工廠對象決定建立出那一種產品類的執行個體。
四.簡單原廠模式的結構
簡單原廠模式是類的建立模式,這個模式的一般性結構如所示。
角色與結構
簡單原廠模式就是由一個工廠類可以根據傳入的參量決定建立出哪一種產品類的執行個體。所示為以一個示意性的實現為例說明簡單原廠模式的結構。
從可以看出,簡單原廠模式涉及到工廠角色、抽象產品角色以及具體產品角色等
三個角色:
(1)工廠類(Creator)角色:擔任這個角色的是Factory 方法模式的核心,含有與應用緊
密相關的商業邏輯。工廠類在用戶端的直接調用下建立產品對象,它往往由一個
具體Java 類實現。
(2)抽象產品(Product)角色:擔任這個角色的類是Factory 方法模式所建立的對象的父
類,或它們共同擁有的介面。抽象產品角色可以用一個Java 介面或者Java 抽象類別
實現。
(3)具體產品(Concrete Product)角色:Factory 方法模式所建立的任何對象都是這個角
色的執行個體,具體產品角色由一個具體Java 類實現。
工廠類的示意性原始碼如下所示。可以看出,這個Factory 方法建立了一個新的具體產品的執行個體並返還給調用者。
代碼清單8:Creator 類的原始碼
public class Creator
{
//靜態Factory 方法
public static Product factory()
{
return new ConcreteProduct();
}
}
抽象產品角色的主要目的是給所有的具體產品類提供一個共同的類型,在最簡單的情況下,可以簡化為一個標識介面。所謂標識介面,就是沒有聲明任何方法的空介面。
代碼清單9:抽象角色Product 介面的原始碼
public interface Product
{
}
具體產品類的示意性原始碼如下。
代碼清單10:具體產品角色ConcreteProduct 類的原始碼
public class ConcreteProduct implements Product
{
public ConcreteProduct(){}
}
雖然在這個簡單的示意性實現裡面只給出了一個具體產品類,但是在實際應用中一般都會遇到多個具體產品類的情況。
五.簡單原廠模式的實現
1.多層次的產品結構
在真實的系統中,產品可以形成複雜的等級結構,比如所示的樹狀結構上就有多個抽象產品類和具體產品類。
這個時候,簡單原廠模式採取的是以不變應萬變的策略,一律使用同一個工廠類。如所示。
圖中從Factory 類到各個Product 類的虛線代表建立(依賴)關係;從Client 到其他類的聯線是一般依賴關係。這樣做的好處是設計簡單,產品類的等級結構不會反映到工廠類中來,從而產品類的等級結構的變化也就不會影響到工廠類。但是這樣做的缺點是,增加新的產品必將導致工廠類的修改。
2. 使用Java 介面或者Java 抽象類別
如果模式所產生的具體產品類彼此之間沒有共同的商業邏輯,那麼抽象產品角色可以由一個Java 介面扮演;相反,如果這些具體產品類彼此之間確有共同的商業邏輯,那麼這些公有的邏輯就應當移到抽象角色裡面,這就意味著抽象角色應當由一個抽象類別扮演。在一個類型的等級結構裡面,共同的代碼應當盡量向上移動,以達到共用的目的,如所示。