對 工廠模式與建造者模式 的個人理解,以及結合運用
學習了一段時間設計模式,就想分享一下自己的理解, 歡迎大家多多指點,指出不足之處哈
個人理解,工廠模式用於處理 如何獲取例項物件 問題,建造者模式用於處理如何建造例項物件 問題(好像是廢話。。。)。兩者應該可以結合起來,下面將以商店售賣手機這場景來描述。
工廠模式:
簡單工廠模式-工廠方法模式:
簡單工廠模式由SimplePhoneFactory,集中獲取不同的手機例項物件。
工廠方法模式由PhoneFactory的兩個實現,分別獲取不同的手機例項物件。
下面貼上程式碼,主要關注工廠類。
public abstract class Phone { /** * 品牌 */ protected String brand; /** * 作業系統 */ protected String os; /** * 充電 */ public abstract void charge(); //GET SET方法... } public class ApplePhone extends Phone { @Override public void charge() { System.out.println("普通充電"); } } public class SonyPhone extends Phone { @Override public void charge() { System.out.println("快充"); } } //////////////////////////////////// public class SimplePhoneFactory { public static Phone getPhone(int brand) { if (brand == 0) { ApplePhone applePhone = new ApplePhone(); applePhone.setBrand("Apple"); return applePhone; } SonyPhone sonyPhone = new SonyPhone(); sonyPhone.setBrand("Sony"); return sonyPhone; } } //////////////////////////////////// public interface PhoneFactory { Phone getPhone(); } public class SonyPhoneFactory implements PhoneFactory { public SonyPhone getPhone() { SonyPhone sonyPhone = new SonyPhone(); sonyPhone.setBrand("Sony"); return sonyPhone; } } public class ApplePhoneFactory implements PhoneFactory { public ApplePhone getPhone() { ApplePhone applePhone = new ApplePhone(); applePhone.setBrand("Apple"); return applePhone; } } //////////////////////////////////// public class StoreA { private int brand; public StoreA(int brand) { super(); this.brand = brand; } /** * 補充手機 */ public void supplyPhone() { Phone phone = SimplePhoneFactory.getPhone(brand); // 補充手機邏輯... System.out.println("補充" + phone.getBrand() + "手機完成"); } public static void main(String[] args) { StoreA storeA = new StoreA(0); storeA.supplyPhone(); } } public class StoreB { private PhoneFactory phoneFactory; public StoreB(PhoneFactory phoneFactory) { super(); this.phoneFactory = phoneFactory; } /** * 補充手機 */ public void supplyPhone() { Phone phone = phoneFactory.getPhone(); // 補充手機邏輯... System.out.println("補充" + phone.getBrand() + "手機完成"); } public static void main(String[] args) { StoreB storeB = new StoreB(new SonyPhoneFactory()); storeB.supplyPhone(); } }
當要新增其它品牌的手機時,
簡單工廠模式的SimplePhoneFactory類需要修改getPhone方法程式碼
工廠方法模式只需增加PhoneFactory的實現即可
小結:一類產品有多種不同例項物件(本例的手機,有不同品牌),當新增一種例項物件時(新增一個品牌的手機),工廠方法模式符合 對擴充套件開放,對修改封閉原則。
方法工廠模式-抽象工廠模式:增加一類產品:耳機,來方便對比。
工廠方法模式-建造者模式:接下來繼續貼上程式碼,注意StoreC、StoreD的main方法。
public abstract class Headset { /** * 品牌 */ protected String brand; abstract void play(); public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } } public class AppleHeadset extends Headset { @Override void play() { // Apple 耳機播放邏輯 ... System.out.println("Apple 耳機播放完成"); } } public class SonyHeadset extends Headset { @Override void play() { // Sony 耳機播放邏輯... System.out.println("Sony 耳機播放完成"); } } //////////////////////////////// public interface HeadsetFactory { Headset getHeadset(); } public class SonyHeadsetFactory implements HeadsetFactory { public SonyHeadset getHeadset() { SonyHeadset sonyHeadset = new SonyHeadset(); sonyHeadset.setBrand("Sony"); return sonyHeadset; } } public class AppleHeadsetFactory implements HeadsetFactory { public AppleHeadset getHeadset() { AppleHeadset appleHeadset = new AppleHeadset(); appleHeadset.setBrand("Apple"); return appleHeadset; } } //////////////////////////////// public interface Factory { Phone getPhone(); Headset getHeadset(); } public class AppleFactory implements Factory { public ApplePhone getPhone() { ApplePhone applePhone = new ApplePhone(); applePhone.setBrand("Apple"); return applePhone; } public AppleHeadset getHeadset() { AppleHeadset appleHeadset = new AppleHeadset(); appleHeadset.setBrand("Apple"); return appleHeadset; } } public class SonyFactory implements Factory { public SonyPhone getPhone() { SonyPhone sonyPhone = new SonyPhone(); sonyPhone.setBrand("Sony"); return sonyPhone; } public SonyHeadset getHeadset() { SonyHeadset sonyHeadset = new SonyHeadset(); sonyHeadset.setBrand("Sony"); return sonyHeadset; } } //////////////////////////////// public class StoreC { private PhoneFactory phoneFactory; private HeadsetFactory headsetFactory; public StoreC(PhoneFactory phoneFactory, HeadsetFactory headsetFactory) { super(); this.phoneFactory = phoneFactory; this.headsetFactory = headsetFactory; } /** * 補充手機 */ public void supplyPhone() { Phone phone = phoneFactory.getPhone(); // 補充手機邏輯... System.out.println("補充" + phone.getBrand() + "手機完成"); } /** * 補充耳機 */ public void supplyHeadset() { Headset headset = headsetFactory.getHeadset(); // 補充耳機邏輯... System.out.println("補充" + headset.getBrand() + "耳機完成"); } public static void main(String[] args) { StoreC storeD = new StoreC(new SonyPhoneFactory(), new SonyHeadsetFactory()); storeD.supplyPhone(); storeD.supplyHeadset(); } } public class StoreD { private Factory factory; public StoreD(Factory factory) { super(); this.factory = factory; } /** * 補充手機 */ public void supplyPhone() { Phone phone = factory.getPhone(); // 補充手機邏輯... System.out.println("補充" + phone.getBrand() + "手機完成"); } /** * 補充耳機 */ public void supplyHeadset() { Headset headset = factory.getHeadset(); // 補充耳機邏輯... System.out.println("補充" + headset.getBrand() + "耳機完成"); } public static void main(String[] args) { StoreD storeC = new StoreD(new SonyFactory()); storeC.supplyPhone(); storeC.supplyHeadset(); } }
從程式碼看,如果要從StoreC,StoreD中,選一個來例項化不同的專賣店,
抽象工廠中的StoreD更適合,只要在構造方法那傳入不同的單個Factory,即可獲得不同的專賣店。
而工廠方法中的StoreC,如果現在有耳機、手機、電腦、電視等等,那用StoreC例項化每個店,都得傳入很多Factory,且得注意這些Factory都是同一品牌的。
小結:多類產品分別有多種不同例項物件(本例的手機,耳機都分別有不同品牌),而跨類別的例項物件有聯絡(本例的聯絡是同一品牌),暫且稱有聯絡的那些例項物件為同一族。那抽象工廠模式可以讓產品使用者更方便獲取同一族的產品。疑問:如果產品使用者沒有獲取同一族產品這需求,那工廠方法模式優點在哪?
我猜,如果不同族的產品線差異很大(如新增電腦,電視這兩類產品,但Apple只有電腦,Sony只有電視),抽象工廠模式會產生很多無意義程式碼。
那抽象工廠模式的 AppleFactory的getTV,SonyFactory的getPC 就得返回null或者其它處理。
而工廠方法模式,只要按需新增ApplePCFactory,SonyTVFactory等。
上面兩個類圖好像似,完全看不出建造者模式的必要。再看看結合建造者模式的工廠方法模式的類圖:
呃。。。這類圖太複雜了,還不如直接對著程式碼說來得方便。主要關注那幾行作了記號(<<<<<<<<<<)的程式碼。
public interface PhoneBuilder {
/**
* 刻銘牌
*/
void buildBrand();
/**
* 安裝系統
*/
void buildOs();
Phone getResult();
}
public class ApplePhoneBuilder implements PhoneBuilder {
private ApplePhone applePhone = new ApplePhone();
public void buildBrand() {
applePhone.setBrand("Apple");
}
public void buildOs() {
applePhone.setOs("IOS");
}
public Phone getResult() {
return applePhone;
}
}
public class SonyPhoneBuilder implements PhoneBuilder {
private SonyPhone sonyPhone = new SonyPhone();
public void buildBrand() {
sonyPhone.setBrand("Sony");
}
public void buildOs() {
sonyPhone.setOs("Android");
}
public Phone getResult() {
return sonyPhone;
}
}
////////////////////////////////
public interface PhoneFactory {
Phone getPhone();
}
//普通的工廠方法模式工廠類
public class SonyPhoneFactory implements PhoneFactory {
public SonyPhone getPhone() {
SonyPhone sonyPhone = new SonyPhone();
sonyPhone.setBrand("Sony");<<<<<<<<<<<<<<<<<<
sonyPhone.setOs("Android");<<<<<<<<<<<<<<<<<<
return sonyPhone;
}
}
public class ApplePhoneFactory implements PhoneFactory {
public ApplePhone getPhone() {
ApplePhone applePhone = new ApplePhone();
applePhone.setBrand("Apple");<<<<<<<<<<<<<<<<<<
applePhone.setOs("IOS");<<<<<<<<<<<<<<<<<<
return applePhone;
}
}
//結合建造者模式的工廠類
public class PhoneDirector {
public Phone construct(PhoneBuilder builder) {
builder.buildBrand();<<<<<<<<<<<<<<<<<<
builder.buildOs();<<<<<<<<<<<<<<<<<<
return builder.getResult();
}
}
public class ApplePhoneFactory implements PhoneFactory {
private PhoneDirector director = new PhoneDirector();
public ApplePhone getPhone() {
ApplePhoneBuilder builder = new ApplePhoneBuilder();
return (ApplePhone) director.construct(builder);
}
}
public class SonyPhoneFactory implements PhoneFactory {
private PhoneDirector director = new PhoneDirector();
public SonyPhone getPhone() {
SonyPhoneBuilder builder = new SonyPhoneBuilder();
return (SonyPhone) director.construct(builder);
}
}
雖然類圖那麼複雜,但從程式碼看,還是很容易看出建造者模式的作用。
普通的工廠方法模式中,同一類產品中不同例項物件的建造過程(SonyPhoneFactory,ApplePhoneFactory中作記號的程式碼),十分相似。那是不是可以抽出來,從而做到程式碼複用。
再看看結合了建造者模式的工廠類,就會明白建造者模式就是把建造過程抽到了導演類中(PhoneDirector中作記號的程式碼)。之所以能夠這樣,多虧了前面的多個Builder類。
又有疑問了,為了複用重複程式碼,卻要新增那麼多類,是不是有點得不償失?
我猜,建造者模式抽取建造過程,不是為了減少程式碼量,而是為了把建造順序統一到一個地方!以後一旦要修改建造順序,也只需要修改導演類即可!
至於 抽象工廠模式-建造者模式,由於原理相似,就不哆嗦了。
小結:當一類產品中不同例項物件的建造過程相似,且未來很有可能要修改建造順序的話,那建造者模式可以處理這種情況。
總結:從 普通的工廠方法模式 到 結合建造者模式的工廠方法模式,對產品使用者(Store類)沒有影響。所以對於產品使用者來說,是否用建造者模式是不知情的,因為產品使用者只關心產品的獲取。所以正如開頭所說,工廠模式用於處理 如何獲取例項物件 問題,建造者模式用於處理 如何建造例項物件 問題。
下篇文章打算在 工廠方法模式-建造者模式 基礎上,再結合 橋接模式,感覺這三個模式的結合運用,在實際中很常見。到時那類圖想想都好恐怖。。。