最近在學習設計模式,正在看design pattern 和大話設計模式這兩本書,感覺大話設計模式這本書確實講的還是很不錯的,簡單通俗易懂,非常適合我這樣的初學者,但是設計模式這個玩意僅僅只是看還是不夠的,感覺看懂非常簡單,但真的自己寫起來還是要費電功夫的,爲了加強自己的記憶,於是我決定自己動手練習一下,順便就謝謝blog記憶一下。
後續我會繼續更新這一系列的blog。(因爲之前沒有學過UML,所以對設計程序架構這一塊實在是不會,看的書上有很多類圖,類圖可以直接反應一個系統的結構,對於這些模式的類圖就只好以後慢慢更新上來了,目前也準備開始慢慢的去學一學UML,現在發現這個東西還真是挺重要的)。好了,廢話不多說,我們開始吧,對於blog中寫得欠佳,或者有誤的地方歡迎大家指正。
工廠模式,顧名思義就是使用一個工廠生產東西。那麼我們就要汽車工廠爲例。我們使用汽車工廠生產各種不同類型的汽車。工廠模式的主要作用是使用一個統一的工廠去控制生產某大類物品中的各種不同的具體物品,比如使用汽車工廠生產奔馳,奧迪,寶馬等等不懂類型的汽車。對於工廠的使用者來說他並不關心汽車是怎麼具體的被生產出來的,那麼我們就要對汽車工廠的使用者隱藏各種不同類型的汽車的細節,使用者只需要簡單的選擇自己需要生產何種類型的車,然後拿到被生產出來的物品就可以了。
汽車的種類那麼多,生產者沒那麼多精力去管理那麼多的生產線,那麼就必須有一個統一的總控制部件來控制不同的生產線來生產各種汽車,這樣我們就必須抽象出一個汽車大類,而其他的具體的種類的汽車繼承這個大類,然後我們就可以對不同種類的車進行統一的管理,同樣不同種類的車應該在自己的生產線上生產出來,啓動哪個生產線進行生產就會產出哪種類型的車,這樣我們就需要一個管理生產線的總控室。這樣各個不同的類之間的關係就可以抽象出來了。
類圖(後續再補充)
汽車大類:
package com.kexin.designpatterns.simpleFactory;
public abstract class Automobile {
/**
* 汽車類型
*/
String carBrand;
/**
* 引擎
*/
String engine;
/**
* 汽車框架
*/
String carFrame;
/**
* 汽車輪胎
*/
String [] wheels;
/**
* 顯示汽車的一些參數
* 其實這個方法也應該被各個不同的汽車的子類繼承的,
* 因爲不同的汽車需要顯示的參數中應該也是有很多不同的。
*/
public void showCar() {
System.out.println("Automotive Parts:");
System.out.println(this.carBrand);
System.out.println(this.carFrame);
System.out.println(this.engine);
System.out.print("wheels:");
for(String s : this.wheels)
{
System.out.print(s + " ");
}
System.out.println();
}
}
具體的汽車類:
package com.kexin.designpatterns.simpleFactory;
public class Audi extends Automobile{
public Audi()
{
this.carBrand = "Audi";
this.carFrame = "Audi Frame";
this.engine = "Audi Engine";
this.wheels = new String[4];
this.wheels[0] = "Audi FrontLeft wheel";
this.wheels[1] = "Audi FrontRight wheel";
this.wheels[2] = "Audi BehindLeft wheel";
this.wheels[3] = "Audi BehindRight wheel";
}
}
package com.kexin.designpatterns.simpleFactory;
public class Benz extends Automobile{
public Benz()
{
this.carBrand = "Benz";
this.carFrame = "Benz Frame";
this.engine = "Benz Engine";
this.wheels = new String[4];
this.wheels[0] = "Benz FrontLeft wheel";
this.wheels[1] = "Benz FrontRight wheel";
this.wheels[2] = "Benz BehindLeft wheel";
this.wheels[3] = "Benz BehindRight wheel";
}
}
package com.kexin.designpatterns.simpleFactory;
public class BMW extends Automobile{
public BMW()
{
this.carBrand = "BMW";
this.carFrame = "BMW Frame";
this.engine = "BMW Engine";
this.wheels = new String[4];
this.wheels[0] = "BMW FrontLeft wheel";
this.wheels[1] = "BMW FrontRight wheel";
this.wheels[2] = "BMW BehindLeft wheel";
this.wheels[3] = "BMW BehindRight wheel";
}
}
具體的汽車生產線類:
package com.kexin.designpatterns.simpleFactory;
/**
* 奧迪工廠
* @author Administrator
*
*/
public class AudiFactory{
private static AudiFactory audiFactory;
public Audi produceAudi() {
System.out.println("A new Audi is under building...");
Audi audi = new Audi();
System.out.println("completed !");
return audi;
}
public static AudiFactory getInstance()
{
if(audiFactory == null)
{
audiFactory = new AudiFactory();
}
return audiFactory;
}
}
package com.kexin.designpatterns.simpleFactory;
/**
* 奔馳工廠
* @author Administrator
*
*/
public class BenzFactory {
private static BenzFactory benzFactory;
public Benz produceBenz() {
System.out.println("A new Benz is under building...");
Benz benz = new Benz();
System.out.println("completed !");
return benz;
}
public static BenzFactory getInstance()
{
if(benzFactory == null)
{
benzFactory = new BenzFactory();
}
return benzFactory;
}
}
package com.kexin.designpatterns.simpleFactory;
public class BMWFactory
{
private static BMWFactory bmwFactory;
public BMW produceBMW()
{
System.out.println("A new Benz is under building...");
BMW bmw = new BMW();
System.out.println("completed !");
return bmw;
}
public static BMWFactory getInstance()
{
if(bmwFactory == null)
{
bmwFactory = new BMWFactory();
}
return bmwFactory;
}
}
具體生產線的統一管理類:
package com.kexin.designpatterns.simpleFactory;
/**
* 抽象汽車工廠
* @author Administrator
* 實際上不同的車型,生產的工序中還是有很多的不同點的,所以要讓不同的汽車工廠
* 都各自實現這個方法,已完成生產不同種類的汽車的需要
*/
public class AutomobileFactory {
private static AutomobileFactory automobileFactory ;
/**
* 提供不同的工廠名字,決定生產何種類型的車。
* 這種方式,每次有新的類型的汽車生產工廠建立起來後就必須改的這個類。
* @param FactoryName
* @return
*/
public Automobile produceAutomobile(String factoryName)
{
Automobile autoMobile = null;
switch(factoryName)
{
case "Audi":
autoMobile = AudiFactory.getInstance().produceAudi();
break;
case "Benz":
autoMobile = BenzFactory.getInstance().produceBenz();
break;
case "BMW":
autoMobile = BMWFactory.getInstance().produceBMW();
break;
}
return autoMobile;
}
public static AutomobileFactory getInstance()
{
if(automobileFactory == null)
{
automobileFactory = new AutomobileFactory();
}
return automobileFactory;
}
}
從上面的代碼我們可以發現這裏有一個缺點:如果我們的工程已經投入使用了,而我們這個時候需要新增一條大衆汽車生產線,那麼我們就必須去修改最後那個統一生產線管理類,這就破壞了軟件設計中的開放閉合原則:開放封閉原則(OCP,Open Closed Principle)是所有面向對象原則的核心。軟件設計本身所追求的目標就是封裝變化、降低耦合,而開放封閉原則正是對這一目標的最直接體現。
放封閉原則,其核心的思想是:
軟件實體應該是可擴展,而不可修改的。也就是說,對擴展是開放的,而對修改是封閉的。因此,開放封閉原則主要體現在兩個方面: 對擴展開放,意味着有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。 對修改封閉,意味着類一旦設計完成,就可以獨立完成其工作,而不要對類進行任何修改。這就是簡單工廠的一個缺點。爲了解決這個問題我對統一工廠管理類進行了一點改進。利用反射(這是對編程語言有依賴的,現在有些面向對象的編程語言並不支持反射,但是java支持),我們在統一管理工廠裏面把具體生產線類的Class對象作爲參數傳進去,通過這個對象來動態生成這個生產線類的實例,然後使用這個實例調用自己的生產方法來生產汽車。這樣當有新的汽車生產線加進來的時候我們只需要傳入這個生產線的Class對象就可以了(前提是你必須有這種新汽車結構模板並且已經爲它搭建好了生產線)而不需要去修改統一管理工廠類了,而傳入的參數是可以有工廠的使用者決定的,這就解決了開發閉合原則被破壞的問題。那麼我們看看具體的代碼吧:package com.kexin.designpatterns.simpleFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class AdvancedCarFactory {
public Automobile produceCar(Class<?> assemblyLineName)
{
Automobile autoMoblie = null;
Method produceMethod = null;
Method [] methods = assemblyLineName.getMethods();
for(Method m : methods)
{
if(m.getName().contains("produce"))
{
produceMethod = m;
}
}
try {
autoMoblie = (Automobile) produceMethod.invoke(assemblyLineName.newInstance(),null);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return autoMoblie;
}
}
現在我們看看使用者如何使用這個工廠吧:
package com.kexin.designpatterns.simpleFactory;
public class CarProducer {
public static void main(String[] args)
{
AutomobileFactory factory = AutomobileFactory.getInstance();
Audi audi = (Audi)factory.produceAutomobile("Audi");
audi.showCar();
Benz benz = (Benz)factory.produceAutomobile("Benz");
benz.showCar();
BMW bmw = (BMW)factory.produceAutomobile("BMW");
bmw.showCar();
Benz benz2 = (Benz)(new AdvancedCarFactory().produceCar(BenzFactory.class));
benz2.showCar();
}
}
從最後這段代碼我們可以看到使用者完全不必去關心各種類型的汽車到底是如何被生產的,他只需要簡單的選擇自己需要生產的汽車的類型,然後其他的工作都交給統一管理工廠去處理就可以了。好了我的第一個設計模式就到這裏吧。