23種設計模式----工廠方法模式----建立型模式
1.什麼是工廠方法模式
將例項的生成交給子類(出自《圖解設計模式》) 這句話怎麼理解? 我自己的理解就是把建立和實現分離。 使用工廠方法,抽象的工廠決定怎麼建立例項,而具體的例項的建立由抽象的工廠的實現去完成。 同樣的的,需要被工廠建立的類也可以抽象為抽象類,抽象類說明這些需要被建立的類的共性,而具體建立的類則是根據需要的抽象類的子類。 很難理解 很難理解 很難理解
2.通俗的解釋
首先建立一個概念: 抽象工廠:決定怎麼建立例項的抽象工廠類 例項工廠:具體執行建立的例項工廠類 抽象產品:需要被建立的類的抽象類共性類 例項產品:真正意義上被建立的類 所以呢,抽象工廠就是使用模板方法的思想。 由抽象的工廠定義建立的方式—關聯抽象產品(模板類) 而例項的工廠實際建立----關聯例項產品(實現類) 所以,這裡又能夠和模板方法對應。
3例子
3.1例子的背景。
車,大家都不陌生。車有自己的屬性,同時每一種車需要有自己的交通規則需要遵守,比如自行車和小汽車,機動車和非機動車。。。。 造車廠,雖然沒有真正的參觀過,但是都聽說過,造車廠裡面的每一種車的建造方式又不相同,有三輪車,小汽車,大巴車。。。。
3.2類的抽象
為了顯示方法的呼叫先後順序等等,建立一個方法類,但是為了增加難度,我們不是使用靜態方法的方式,而是使用繼承的方式:
public class Show { public static void show(boolean b,String name){ System.out.print("#####################################"); System.out.print(name + (b?"開始":"結束")); System.out.println("######################################"); } }
根據背景。所有的車有一定的共性:一些基本的屬性和需要遵守的交通規則,所以,可以得到車的抽象類
/** * 定義車有型別和寬和高三個屬性 * 定義車的例項建立後,車的屬性無法修改 * 定義所有的車必須遵守交通規則 * 設定,預設屬性的全參構造 */ public abstract class Car extends Show{ private String type; private Double width; private Double height; public Car(String type,Double width,Double height){ this.type = type; this.width = width; this.height = height; } public abstract void trafficRuler(); public String getType() { return type; } public Double getWidth() { return width; } public Double getHeight() { return height; } }
有了抽象的車,那麼抽象的造車廠也就必須有:
/**
* 定義一個車的工廠
* 定義建立車的三個步驟:
* --doType
* --doWidth
* --doHeight
* 給車定義一個預設的交通規則:靠右行
*/
public abstract class Factory extends Show{
protected abstract void doType();
protected abstract void doWidth();
protected abstract void doHeight();
private Car car;
public Car create(String type,Double width,Double height){
super.show(true, "Factory#create");
car = getCar(type,width,height);
doType();
doWidth();
doHeight();
super.show(false, "Factory#create");
return car;
}
protected abstract Car getCar(String type,Double width,Double height);
}
稍微總結一下。 抽象產品和抽象工廠都有了 在抽象工廠中關聯了抽象產品,需要特別理解的就是為什麼工廠方法是模板方法的基礎上設計的。 在抽象工廠中,使用了一個抽象的方法getCar,這個抽象的方法是在抽象工廠的子類中進行實現的,所以,這裡是一個模板方法的使用。
3.3例項類的抽象
有了抽象的產品和抽象的工廠,接下來就是根據需要,實現例項的工廠個例項的產品。 假設我們需要的車是寶馬車,工廠是寶馬的工廠:
public class BMWCar extends Car{
public BMWCar(String type, Double width, Double height) {
super(type, width, height);
}
@Override
public void trafficRuler() {
super.show(true, "BMWCar#trafficRuler");
System.out.println("BMWCar#traffic");
super.show(false, "BMWCar#trafficRuler");
}
}
寶馬車有自己的三個基本屬性之外,還有自己的交通規則,比如寶馬車只能在公路上行駛,不能再人行道上行駛,不能上天橋。。。。 寶馬車的例項工廠:
public class BMWFactory extends Factory{
private BMWCar bmwCar;
public BMWCar create(String type,Double width,Double height,String carName){
super.show(true, "Factory#create");
bmwCar = (BMWCar) getCar(type,width,height);
doType();
doWidth();
doHeight();
super.show(false, "Factory#create");
return bmwCar;
}
@Override
protected void doType() {
super.show(true, "BMWFactory#doType");
System.out.println("BMW#doType");
super.show(false, "BMWFactory#doType");
}
@Override
protected void doWidth() {
super.show(true, "BMWFactory#doWidth");
System.out.println("BMW#doWidth");
super.show(false, "BMWFactory#doWidth");
}
@Override
protected void doHeight() {
super.show(true, "BMWFactory#doHeight");
System.out.println("BMW#doHeight");
super.show(false, "BMWFactory#doHeight");
}
@Override
protected Car getCar(String type,Double width,Double height) {
return new BMWCar(type,width,height);
}
}
例項工廠中實現了我們抽象工廠中的getCar的抽象方法,這裡就是模板方法的實現類 同樣呢,可以這麼理解: 抽象工廠建立的是抽象產品 例項工廠建立的是例項產品 抽象和例項是完全分離的 沒有例項,抽象部分也不會報錯,但是不能使用,因為有抽象方法未實現(除非使用內部類的方式實現抽象方法) 例項部分,操作的都是例項相關的變數,和抽象部分沒有關係。實現抽象和例項分離。
3.4測試
具體怎麼使用呢?
public class Main {
public static void main(String[] args) {
Factory factory = new BMWFactory();
BMWCar car = (BMWCar)factory.create("SUV", 2.3, 1.7);
car.trafficRuler();
}
}
我們首先需要一個例項工廠,然後傳入引數建立一個例項物件: 所以,首先獲得一個例項的工廠,接下來,建立一個例項物件。 輸出結果:
#####################################Factory#create開始######################################
#####################################BMWFactory#doType開始######################################
BMW#doType
#####################################BMWFactory#doType結束######################################
#####################################BMWFactory#doWidth開始######################################
BMW#doWidth
#####################################BMWFactory#doWidth結束######################################
#####################################BMWFactory#doHeight開始######################################
BMW#doHeight
#####################################BMWFactory#doHeight結束######################################
#####################################Factory#create結束######################################
#####################################BMWCar#trafficRuler開始######################################
BMWCar#traffic
#####################################BMWCar#trafficRuler結束######################################
3.5反思
發現這樣還是沒有什麼變化,甚至複雜化了問題,比如,我們的工廠方法中同樣使用了new的關鍵字,雖然不是直接new一個BMW的物件,但是new了一個BMW的工廠,然後通過工廠來建立的物件。 首先,這樣做有一個好處:模板方法的好處,我們可以在工廠做一些除了建立例項之外的操作,比如對建立的例項進行初始化操作之類的。 第二,我們可以先行定義產品如何建立----模板方法。
4.工廠方法的擴充套件
為什麼使用工廠方法,就是為了更好的擴充套件,使用更加分離的程式碼。 接下來我們的需求是自行車。 首先建立自行車的例項產品:
public class Bike extends Car{
public Bike(String type, Double width, Double height) {
super(type, width, height);
}
@Override
public void trafficRuler() {
super.show(true, "Bike#trafficRuler");
System.out.println("Bike#traffic");
super.show(false, "Bike#trafficRuler");
}
}
接下來是自行車的例項工廠:
public class BikeFactory extends Factory{
private Bike bike;
public Bike create(String type,Double width,Double height,String carName){
super.show(true, "Factory#create");
bike = (Bike) getCar(type,width,height);
doType();
doWidth();
doHeight();
super.show(false, "Factory#create");
return bike;
}
@Override
protected void doType() {
super.show(true, "BikeFactory#doType");
System.out.println("BikeFactory#doType");
super.show(false, "BikeFactory#doType");
}
@Override
protected void doWidth() {
super.show(true, "BikeFactory#doWidth");
System.out.println("BikeFactory#doWidth");
super.show(false, "BikeFactory#doWidth");
}
@Override
protected void doHeight() {
super.show(true, "BikeFactory#doHeight");
System.out.println("BikeFactory#doHeight");
super.show(false, "BikeFactory#doHeight");
}
@Override
protected Car getCar(String type, Double width, Double height) {
return new Bike(type, width, height);
}
}
5測試
測試只需要在之前的測試方法中新增:
//自行車
factory = new BikeFactory();
Bike bike = (Bike)factory.create("鳳凰", 0.2, 1.3);
bike.trafficRuler();
6測試結果
#####################################Factory#create開始######################################
#####################################BMWFactory#doType開始######################################
BMW#doType
#####################################BMWFactory#doType結束######################################
#####################################BMWFactory#doWidth開始######################################
BMW#doWidth
#####################################BMWFactory#doWidth結束######################################
#####################################BMWFactory#doHeight開始######################################
BMW#doHeight
#####################################BMWFactory#doHeight結束######################################
#####################################Factory#create結束######################################
#####################################BMWCar#trafficRuler開始######################################
BMWCar#traffic
#####################################BMWCar#trafficRuler結束######################################
#####################################Factory#create開始######################################
#####################################BikeFactory#doType開始######################################
BikeFactory#doType
#####################################BikeFactory#doType結束######################################
#####################################BikeFactory#doWidth開始######################################
BikeFactory#doWidth
#####################################BikeFactory#doWidth結束######################################
#####################################BikeFactory#doHeight開始######################################
BikeFactory#doHeight
#####################################BikeFactory#doHeight結束######################################
#####################################Factory#create結束######################################
#####################################Bike#trafficRuler開始######################################
Bike#traffic
#####################################Bike#trafficRuler結束######################################
7.問題
進一步反思,發現這樣做還是有問題: 在測試方法中,我們new了不同的例項工廠,我們能不能抽象一個工具類,使用反射的方式來建立例項工廠,因為使用反射,那麼,對於未來抽象工廠所有的子類,我們只需要使用工具類即可。(未實現,後續有機會增加)