設計模式與實踐:創建型模式(工廠模式)

創建型模式

  • 單例模式
  • 工廠模式
  • 建造者模式
  • 原型模式
  • 對象池模式

工廠模式

所謂工廠,我們可以理解爲一個用來生產實例的工具,工廠負責生產一系列同一性質的產品。在調用的時候,我們把這個工廠看做一個黑盒,我們只要告訴它我們需要什麼實例,他就會給我們創建對應的實例。

1. 靜態工廠模式

我們寫一個簡單地工廠類來創建Vehicle實例。我們創建一個抽象Vehicle類和繼承自它的三個具體類:Bike、Car和Truck。 工廠類(也叫做靜態工廠類),代碼如下:

public class VehicleFactory{
	//用來讓開發者選擇需要創建的對象的標誌
	public enum VehicleType{
		Bike,
		Car,
		Truck;
	}
	public static Vehicle create(VehicleType type){
		if(type.equals(Vehicle.Bike) return new Bike();
		if(type.equals(Vehicle.Car) return new Car();
		if(type.equals(Vehicle.Truck) return new Truck();
		else return null;//這個else針對的是最後一個if
	}
}

這個靜態工廠類邏輯簡單,有好的地方:它只負責Vehicle類的實例化,符合單一職責原則,用戶只需要調用Vehicle接口,符合了依賴倒置原則。但是不能很好的適應市場環境,真實開發過程中,可能會增加新的Vehicle類,這時候每增加一個Vehicle類就要對該VehicleFactory進行一次修改,這就違反了開閉原則(已經寫好的類最好不要進行修改,只進行拓展)

所以我們需要對其進行修改,另其符合開閉原則,實現方法有下面兩種:

  • 適用反射機制註冊產品類對象和實例化
  • 註冊產品對象並向每個產品添加newInstance()方法,該方法返回與自身類型相同的新實例。

1.1 使用反射機制進行類註冊的簡單工廠模式

爲此,我們需要使用map對象來保存產品ID以及它對應的類

private Map<String,Class> registerdProducts = new HashMap<String,Class>();

然後,增加一個註冊新Vehicle類的方法:

public void registerVehicle(String vehicleId,Class vehicleClass){
	registerdProducts.put(vehicleId,vehicleClass);
}

構造方法如下:

public Vehicle createVehicle(String type) throws InstantiationException,IllegalAccessException{
	Class productClass = registerdProducts.get(type);
	return (Vehicle) productClass.newInstance();
}

我們知道,使用反射會使得程序運行效率降低,有的情況下,反射機制並不適用,比如反射機制需要運行時權限,這導致有的特定環境下,反射獲得實例無法實現。

在對性能要求很高的場景下,應該儘量避免使用這種機制。

1.2 使用newInstance方法進行類註冊的簡單工廠模式

每個產品類都可以創建自己的實例

首先在Vehicle基類中添加一個抽象方法:

abstract public Vehicle newInstance();

對於每種產品,基類(父類)聲明爲抽象的方法都要實現

@Override
public Car newInstance(){
	return new Car();
}

在工廠類中,更改map用於保存對象的ID以及它對應的Vehicle對象

private Map<String,Vehicle> registerdProducts = new HashMap<String,Vehicle>();

通過實例註冊一種新的Vehicle類型:

public void registerVehicle(Sring vehicleId, Vehicle vehicle){
	registerdProducts.put(vehicleId,vehicle);
}

也要對應地改變Factory中的createVehicle方法:

public Vehicle createVehicle(String vehicleId){
	return registerdProducts.get(vehicleId).newInstance();
}

調用方法:

public class test{
	public static void main(String[] args){
		registerVehicles();

		//動態註冊以後,就可以隨時讓工廠生產該實例
		Car car = (Car)VehicleFactory.createVehicle("Car");
	}

	pulic static void registerVehicles(){
		VehicleFactory.registerVehicle("Car",new Car());
		VehicleFactory.registerVehicle("Truck",new Truck());
	}
}

2. 工廠方法模式

工廠方法模式是在靜態工廠模式上的改進。工廠類被抽象化,使用實例化特定產品類的代碼被轉移到實現抽象方法的子類中。這樣不需要修改就可以拓展工廠類。

我們在抽象工廠類中不實現具體的實例化代碼,而是交給抽象工廠類的實現類來完成,這樣如果要拓展更多種實例,就只需要添加抽象工廠類的新實現,而不需要修改它,符合開閉原則。

比如我們有一個汽車工廠,目前只有兩種車型,小型跑車和大型家用車,在軟件中,顧客可以自由決定買什麼樣的車,首先小型車對應的Vehicle類的子類爲SportCar,大型家用車對應的Vehicle子類爲SedanCar。

創建Vehicle類結構之後就可以創建抽象工廠類。注意工廠類中沒有創建實例的代碼。

public abstract class VehicleFactory{

	//只允許子類訪問,對外界隱藏,創建Vehicle,傳入對應size參數
	protected abstract Vehicle createVehicle(String size);
	
	public Vehicle orderVehicle(String size, String color){
		Vehicle vehicle = createVehicle(size);
		vehicle.setColor(color);
		return vehicle;
	}
}

爲了增加汽車實例化的代碼,我們創建了VehicleFactory的子類,也就是CarFactory類,並且在CarFactory中實現從父類中調用的createVehicle抽象方法。實際上,我們可以理解爲,VehicleFactory是一個總公司,它將具體的實例化需求委託給了分工廠。

public class CarFactory extends VehicleFactory{
	@Override
	protected Vehicle createVehicle(String size){
		if(size.equals("small") return new SportCar();
		else if(size.equals("big") return new SedanCar();
		return null;
	}
}

如此一來,在客戶端,我們只需要創建工廠類並創建訂單就可以了

VehicleFactory carFactory = new CarFactory();
carFactory.orderVehicle("big","blue");

如果我們另外增加了業務,比如還需要生產卡車,那我們就創建一個卡車工廠TruckFactory,負責卡車的實例化操作。

public class TruckFactory extends VehicleFactory{
	@Override
	protected Vehicle createVehicle(String size){
		if(size.equals("small")) return SmallTruck();
		else if(size.equals("big") return BigTruck();
		return null;
	}
}

調用手段一致

VehicleFactory truckFactory = new TruckFactory();
truckFactory.orderVehicle("small");

如果說業務量很少,那麼我們不需要新建一個類來作爲大工廠,只需要一個臨時工廠,這裏我們通過匿名具體工廠模式來實現。

Vehicle Factory tempFactory = new VehicleFactory(){
	@Override
	protected Vehicle createVehicle(String size){
		if(size.equals("small") return new SmallTempCar();
		else if(size.equals("big") return new BigTempCar();
		return null;
	}
}

tmepFactory.orderVehicle("big","blue");

3. 抽象工廠模式

抽象工廠模式工廠方法模式的擴展版本,它不再時創建單一類型的對象,而是創建一系列相關聯的對象。如果說工廠方法模式中只包含一個抽象產品類,那麼抽象工廠模式則包含多個抽象產品類。

注意到,抽象工廠模式是創建一系列相關聯的對象。

比如題庫工廠,語文方面有語文課本,語文天天練練習冊,語文筆記本,語文單元卷,這是一系列相關聯的對象,對應的英語方面,也有英語課本,英語天天練練習冊,英語筆記本,英語單元卷。

用於實例化一系列相關聯類的工廠繼承自抽象工廠。

我們有抽象工廠類,規範了我們要創建對象的方法

public abstract class AbstractFactory{
	public AbstractTextBook createTextBook();
	public AbstractExerciseBook createExerciseBook();
	public AbstractNoteBook createNoteBook();
	public AbstractTestPaper createTestPaper();
}

接下來比如我們的目標是語文工廠,那麼我們先定一下語文的相關類
CNTextBook類、CNExerciseBook類、CNNoteBook類、CNTestPaper類。

然後新建一個語文工廠負責創建該系列類

public class CNFactory extends AbstractFactory{
	@Override
	public AbstractTextBook createTextBook(){
		return new CNTextBook();
	}

	@Override
	public AbstractExerciseBook createExerciseBook(){
		return new CNExerciseBook();
	}

	@Override
	public AbstractNoteBook createNoteBook(){
		return new CNNoteBook();
	}

	@Override
	public AbstractTestPaper createTestPaper(){
		return new CNTestPaper();
	}
}

參考:
《Java設計模式及實踐》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章