設計模式-工廠模式
工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。
工廠模式主要是爲創建對象提供了接口。工廠模式按照《Java與模式》中的提法分爲三類:
- 簡單工廠模式(Simple Factory)
- 工廠方法模式(Factory Method)
- 抽象工廠模式(Abstract Factory)
簡單工廠模式不是我們常說的23種設計模式中的一種,那個23種設計模式中的工廠模式只包含工廠方法和抽象工廠。
下面我們就介紹一下,這三種模式。
簡單工廠
簡單工廠模式又叫靜態工廠方法模式(Static FactoryMethod Pattern),是通過專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類。
概念:說的通俗些就是工廠根據不同的參數創建對應的產品。
/**
* 抽象產品 Car
* @author hobart
*/
public abstract class Car {
private String name;
public abstract void drive();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 具體產品 Bmw
* @author hobart
*/
public class Bmw extends Car {
public void drive() {
System.out.println(this.getName()+"車----已啓動-----------------");
}
}
/**
* 具體產品 Benz
* @author hobart
*/
public class Benz extends Car {
public void drive() {
System.out.println(this.getName()+"車----已啓動-----------------");
}
}
/**
* 不存在的產品 Bhq
* @author hobart
*/
public class Bhq extends Car {
void drive() {
System.out.println(this.getName()+"車----不存在-----------------");
}
}
/**
* 簡單工廠
* @author hobart
*/
public class DriverSimpleFactory {
public static Car createCar(String car){
Car c = null;
if("Benz".equalsIgnoreCase(car)) {
c = new Benz();
} else if("BMW".equalsIgnoreCase(car)) {
c = new Bmw();
} else {
c = new Bhq();
}
return c;
}
}
/**
* 測試
* @author hobart
*/
public class DriveTest {
public static void main(String[] args) {
Car car = DriverSimpleFactory.createCar("benz");
car.setName("bmw");
//司機開着bmw出發
car.drive();
}
}
UML圖
優點:
簡單工廠包含邏輯判斷,根據客戶端實例化相關類,去除與具體產品依賴,客戶端不管哪個類的實例,把需求給工廠,工廠單獨創建相應實例。是優點也是不足。它方便擴展算法,比如增加一個開根號的功能,我們只要繼續繼承運算類就行了,同時客戶端也就是使用者不知道具體的實現細節,只要給出相關標示符,工廠函數就馬上給他創建一個他想要的實體就行了。減少了使用者和功能開發者之間的耦合度。
缺點:
比較明顯,在進行擴展的時候,需要修改工廠類的那個分支語句Switch,這樣便違背OCP原則[開閉原則(Open Closed Principle)]
,而且當有多級結構繼承的時候,簡單工廠就會因爲只能對應平行一層記得繼承,不得不使得好多類繼承同一個接口,然後得到A*B這麼多的工廠實體可能,工廠函數很難維護。
工廠方法
定義一個用於創建對象的接口,讓子類決定實例化哪一個類,工廠方法使得一個類的實例化延遲到了子類。
概念:工廠方法有別於簡單工廠卻是簡單工廠的升級。
不同之處:
簡單工廠
是通過參數來控制產品的生產、這裏使用的是重載。不同的工廠實現同一個工廠方法生產不同的產品。
工廠方法
是一個工廠生產一個產品(一對一)。如需增加產品、首先要增加工廠。是一對一的生產對象的模式,更符合OCP原則(開閉原則)。
/**
* 抽象產品 Car
* @author hobart
*/
public abstract class Car{
private String name;
public abstract void drive();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 具體產品 Bmw
* @author hobart
*/
public class Bmw extends Car {
public void drive() {
System.out.println(this.getName()+"車----已啓動-----------------");
}
}
/**
* 具體產品 Benz
* @author hobart
*/
public class Benz extends Car {
public void drive() {
System.out.println(this.getName()+"車----已啓動-----------------");
}
}
/**
* 不存在的產品 Bhq
* @author hobart
*/
public class Bhq extends Car {
public void drive() {
System.out.println(this.getName()+"車----是無法生產的車---------");
}
}
/**
* 抽象工廠
* @author hobart
*/
public abstract class DriverFactoryMethod {
public abstract Car createCar(String car);
}
/**
* 具體工廠 BenzDriver(每個具體工廠負責一個具體產品)
* @author hobart
*/
public class BenzDriverFactoryMethod extends DriverFactoryMethod {
@Override
public Car createCar(String car) {
if("Benz".equalsIgnoreCase(car)) {
return new Benz();
} else {
return new Bhq();
}
}
}
/**
* 具體工廠 BmwDriver(每個具體工廠負責一個具體產品)
* @author hobart
*/
public class BmwDriverFactoryMethod extends DriverFactoryMethod {
@Override
public Car createCar(String car) {
if("BMW".equalsIgnoreCase(car)) {
return new Bmw();
} else {
return new Bhq();
}
}
}
/**
* 測試
* @author hobart
*/
public class DriveTest {
public static void main(String[] args) {
DriverFactoryMethod d = new BenzDriverFactoryMethod();
Car car = d.createCar("benz");
car.setName("benz");
//司機開着benz出發
car.drive();
}
}
UML圖
優點:
遵守開閉原則,直接添加具體產品類和相應工廠類,實例化哪一個工廠放在客戶端, 算法實體的創建被延遲到了工廠子類裏,我們不在工廠裏直接創建對象,而是直接封裝一個一個的小工廠,每個工廠負責創建自己的子類,這樣就不存在switch的情況,也就不存在擴展不滿足OCP的這個問題。
缺點:
第一:增加一個產品就要增加一個產品工廠類,額外開發.
第二:把簡單工廠內部邏輯移到客戶端,所以之前修改工廠類,現在修改客戶端,問題還是存在.
如果算法種類很多,那麼繼承抽象工廠的子類也就會很多,不是很好維護,同時不支持產品切換,比如我們要開發PC,分爲多個系統,那麼我們可以把所有的系統都抽象出來,然後我們在抽象出來工廠,但是如果這個時候我們有兩個硬件呢,PC和Phone,雖然我們可以保證只有這兩個硬件了,但是如果用基本的抽象工廠去實現的話還是很彆扭。
抽象工廠
抽象工廠模式(Abstract Factory Pattern)提供一個創建一系列相關或者相互依賴對象的接口,而無需指定它們具體的類,它是圍繞一個超級工廠創建其他工廠。該超級工廠又稱爲其他工廠的工廠。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
在抽象工廠模式中,接口是負責創建一個相關對象的工廠,不需要顯式指定它們的類。每個生成的工廠都能按照工廠模式提供對象。
概念:說的明白些抽象工廠就是 簡單工廠+工廠方法。
/**
* 抽象產品 Car
* @author hobart
*/
public interface Car{
public abstract void drive();
}
/**
* 具體產品 Bmw
* @author hobart
*/
public class Bmw implements Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void drive(){
System.out.println(this.getName()+"車----已啓動-----------------");
}
}
/**
* 具體產品 Benz
* @author hobart
*/
public class Benz implements Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void drive(){
System.out.println(this.getName()+"車----已啓動-----------------");
}
}
/**
* 抽象產品 Audi
* @author hobart
*/
public class Audi implements Car {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void drive(){
System.out.println(this.getName()+"車----已啓動-----------------");
}
}
/**
* 抽象工廠
* @author hobart
*/
public abstract class DriveAbstractFactory {
public abstract Benz createBenzCar(String car);
public abstract Bmw createBmwCar(String car);
public abstract Audi createAudiCar(String car);
}
/**
* 具體工廠 BmwDriverAbstractFactory(每個具體工廠負責一個具體產品)
* @author hobart
*/
public class BmwDriveAbstractFactory extends DriveAbstractFactory {
@Override
public Bmw createBmwCar(String car) {
Bmw bmw = new Bmw();
bmw.setName(car);
return bmw;
}
@Override
public Benz createBenzCar(String car) {
return null;
}
@Override
public Audi createAudiCar(String car) {
return null;
}
}
/**
* 具體工廠 BenzDriverAbstractFactory(每個具體工廠負責一個具體產品)
* @author hobart
*/
public class BenzDriveAbstractFactory extends DriveAbstractFactory {
@Override
public Benz createBenzCar(String car) {
Benz benz = new Benz();
benz.setName(car);
return benz;
}
@Override
public Bmw createBmwCar(String car) {
return null;
}
@Override
public Audi createAudiCar(String car) {
return null;
}
}
/**
* 具體工廠 AudiDriverAbstractFactory(每個具體工廠負責一個具體產品)
* @author hobart
*/
public class AudiDriveAbstractFactory extends DriveAbstractFactory {
@Override
public Benz createBenzCar(String car) {
return null;
}
@Override
public Bmw createBmwCar(String car) {
return null;
}
@Override
public Audi createAudiCar(String car) {
Audi audi = new Audi();
audi.setName(car);
return audi;
}
}
/**
* 創建工廠的工廠 CarFactory(創建一個具體的工廠)
* @author hobart
*/
public class CarFactory {
public static DriveAbstractFactory driverAbstractFactory = null;
public static DriveAbstractFactory getFactory(String context) {
if(context.equalsIgnoreCase("bmw")) {
return new BmwDriveAbstractFactory();
} else if(context.equalsIgnoreCase("benz")) {
return new BenzDriveAbstractFactory();
} else if(context.equalsIgnoreCase("audi")) {
return new AudiDriveAbstractFactory();
} else {
return null;
}
}
}
/**
* 測試
* @author hobart
*/
public class DriveTest {
public static void main(String[] args) {
//獲取car工廠
DriveAbstractFactory driveAbstractFactory = CarFactory.getFactory("BENZ");
//獲取形狀爲 Benz 的對象
Benz benz = driveAbstractFactory.createBenzCar("BENZ");
//調用 Benz 的 drive 方法
benz.drive();
//獲取car工廠
DriveAbstractFactory BmwDriveAbstractFactory = CarFactory.getFactory("BMW");
//獲取形狀爲 Bmw 的對象
Bmw bmw = BmwDriveAbstractFactory.createBmwCar("BMW");
//調用 Benz 的 drive 方法
bmw.drive();
}
}
UML圖
優點:
首先是滿足OCP的,而且可以滿足產品切換,能實現的前提是比如A和B兩個產品,它們有1和2兩個方法接口(類),現在我們在增加新的產品C(假設也是隻有1和2兩個方法接口),我們要做的只是增加一個產品類再增加一個工廠類就行了,如果是簡單工廠或者是工廠方法的的話通常都是增加兩個算法類C.1,C.2,簡單工廠需要修改switch增加兩個語句,工廠方法是在增加兩個工廠類。可見抽象工廠的優點。
缺點:
顯而易見,太重了。
總括:
簡單工廠:
工廠類中,根據條件決定一個接口由哪個具體產品類來實現。
工廠方法:
創建多個工廠類。各個工廠類中,都對應一個獲得接口A實例的方法。用戶決定使用哪個工廠。
抽象工廠:
對工廠方法進行擴展。各個工廠類中,再增加一個獲得接口B實例的方法。
從對象的角度來看:
- 簡單工廠是一對多的關係 (一個工廠可以生成多個產品)
- 工廠方法是一對一的關係(一個工廠只能生產一個產品)
- 抽象工廠抽象了前面兩個設計模式、(一生產線可以生產多個產品可以、多個生產線可以生產一個產品)
對比:
簡單工廠實現簡單,擴展也很容易,但是不滿足OCP,不滿足OCP的代價就是難維護,在維護的時候容易引發新的BUG,相比之下,工廠方法則是把對象的實例化延遲到了繼承的子類裏面,這樣可以量或的擴展工廠。擴展的是時候滿足OCP,但是不支持產品切換,也就是隻能滿足一層的產品(算法)抽象,而抽象工廠則是繼續把產品進行再次抽象,最後得到一個可以支持產品切換的結構,但問題是太重了,過於複雜,不過還好,很多支持反射的語言,我們可以直接通過反射技術來優化這個“過重”的缺點。當然,也可以用反射來優化前面兩個工廠結構(但是抽象工廠和工廠方法相比,兩者也都只是支持一個地方的可擴展而已,不要誤解爲抽象工廠可以擴展兩個地方)。