設計模式-三種工廠模式簡介

設計模式-工廠模式

工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。

工廠模式主要是爲創建對象提供了接口。工廠模式按照《Java與模式》中的提法分爲三類:

  1. 簡單工廠模式(Simple Factory)
  2. 工廠方法模式(Factory Method)
  3. 抽象工廠模式(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實例的方法。

從對象的角度來看:

  1. 簡單工廠是一對多的關係 (一個工廠可以生成多個產品)
  2. 工廠方法是一對一的關係(一個工廠只能生產一個產品)
  3. 抽象工廠抽象了前面兩個設計模式、(一生產線可以生產多個產品可以、多個生產線可以生產一個產品)

對比:
簡單工廠實現簡單,擴展也很容易,但是不滿足OCP,不滿足OCP的代價就是難維護,在維護的時候容易引發新的BUG,相比之下,工廠方法則是把對象的實例化延遲到了繼承的子類裏面,這樣可以量或的擴展工廠。擴展的是時候滿足OCP,但是不支持產品切換,也就是隻能滿足一層的產品(算法)抽象,而抽象工廠則是繼續把產品進行再次抽象,最後得到一個可以支持產品切換的結構,但問題是太重了,過於複雜,不過還好,很多支持反射的語言,我們可以直接通過反射技術來優化這個“過重”的缺點。當然,也可以用反射來優化前面兩個工廠結構(但是抽象工廠和工廠方法相比,兩者也都只是支持一個地方的可擴展而已,不要誤解爲抽象工廠可以擴展兩個地方)。

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