1. 程式人生 > >對 工廠模式與建造者模式 的個人理解,以及結合運用

對 工廠模式與建造者模式 的個人理解,以及結合運用

學習了一段時間設計模式,就想分享一下自己的理解, 歡迎大家多多指點,指出不足之處哈

個人理解,工廠模式用於處理 如何獲取例項物件 問題,建造者模式用於處理如何建造例項物件 問題(好像是廢話。。。)。兩者應該可以結合起來,下面將以商店售賣手機這場景來描述。

工廠模式

簡單工廠模式-工廠方法模式

簡單工廠模式由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類)沒有影響。所以對於產品使用者來說,是否用建造者模式是不知情的,因為產品使用者只關心產品的獲取。所以正如開頭所說,工廠模式用於處理 如何獲取例項物件 問題,建造者模式用於處理 如何建造例項物件 問題。

下篇文章打算在 工廠方法模式-建造者模式 基礎上,再結合 橋接模式,感覺這三個模式的結合運用,在實際中很常見。到時那類圖想想都好恐怖。。。