工廠模式:工廠方法模式定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
一、簡單工廠模式:
實例化對象的時候不再使用 new Object () 形式,可以根據用戶的選擇條件來實例化相關的類。對於客戶端來說,去除了具體的類的依賴。只需要給出具體實例的描述給工廠,工廠就會自動返回具體的實例對象。
【未使用工廠模式】
public abstract class Car {
public abstract void run();
}
public class Benz extends Car {
@Override
public void run() {
System.out.println("開奔馳");
}
}
public class Bmw extends Car{
@Override
public void run() {
System.out.println("開寶馬");
}
}
public class Client {
public static void main(String[] args) {
Car car1 = new Benz();
car1.run();
Car car2 = new Bmw();
car2.run();
}
}
在 Client 類中既有創建對象邏輯又有調用方法邏輯,且與多個類發生耦合。違背了單一職責原則和迪米特法則。
- 單一職責原則(SRP):就一個類而言,應該僅有一個引起它變化的原因。即一個類中應該只有一類邏輯。
- 迪米特法則:如果兩個類不必彼此直接通信,那麼這兩個類就不應當發生直接的相互作用。如果其中一個類需要調用另一個類的某一方法的話,可以通過第三者轉發這個調用
簡單工廠模式屬於類的創建型模式,又叫做靜態工廠方法模式。通過專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類。
【簡單工廠模式】
// 創建生產工廠類
package designpatterns.factory.simple;
public class CarFactory {
public Car createCar(String name) {
Car car = null;
switch (name) {
case "benz":
car = new Benz();
break;
case "bmw":
car = new Bmw();
break;
}
return car;
}
}
// 測試環境
package designpatterns.factory.simple;
public class Client {
public static void main(String[] args) {
CarFactory carFactory = new CarFactory();
Car car = carFactory.createCar("benz");
car.run();
Car car1 = carFactory.createCar("bmw");
car1.run();
}
}
Client 類與 Car 的子類解耦,它不需要知道 Car 實例具體創建細節,只需要通知工廠類需要怎樣的實例。這樣,減少了與外部類(Car 的子類)的通信,遵循了迪米特法則。我們可以對創建的對象進行一些 “加工” ,而且客戶端並不知道,因爲工廠隱藏了這些細節。如果,沒有工廠的話,那我們是不是就得自己在客戶端上寫這些代碼,這就好比本來可以在工廠裏生產的東西,拿來自己手工製作,不僅麻煩以後還不好維護。
但是缺點也很明顯:如果需要在方法裏寫很多與對象創建有關的業務代碼,而且需要的創建的對象還不少的話,我們要在這個簡單工廠類裏編寫很多個方法,每個方法裏都得寫很多相應的業務代碼,而每次增加子類或者刪除子類對象的創建都需要打開這簡單工廠類來進行修改。這會導致這個簡單工廠類很龐大臃腫、耦合性高,而且增加、刪除某個子類對象的創建都需要打開簡單工廠類來進行修改代碼也違反了開 - 閉原則。
二、工廠模式
這時候就需要使用工廠模式了。工廠方法模式是對簡單工廠模式進一步的解耦,因爲在工廠方法模式中是一個子類對應一個工廠類,而這些工廠類都實現於一個抽象接口。這相當於是把原本會因爲業務代碼而龐大的簡單工廠類,拆分成了一個個的工廠類,這樣代碼就不會都耦合在同一個類裏了。
// 工廠抽象類
package designpatterns.factory.general;
public interface CarFactory {
Car createCar();
}
//工廠抽象類的實現
package designpatterns.factory.general;
public class CreateBenz implements CarFactory {
@Override
public Car createCar() {
Car car = new Benz();
return car;
}
}
package designpatterns.factory.general;
public class CreateBmw implements CarFactory {
@Override
public Car createCar() {
Car car = new Bmw();
return car;
}
}
// 測試環境
package designpatterns.factory.general;
public class Client {
public static void main(String[] args) {
CarFactory carBenz = new CreateBenz();
Car car1 = carBenz.createCar();
car1.run();
CarFactory carBmw = new CreateBmw();
Car car2 = carBmw.createCar();
car2.run();
}
}
工廠模式中,要增加產品類時也要相應地增加工廠類,客戶端的代碼也增加了不少。工廠方法把簡單工廠的內部邏輯判斷轉移到了客戶端代碼來進行。違背責任單一原則。
你想要加功能,本來是改工廠類的,而現在是修改客戶端。而且各個不同功能的實例對象的創建代碼,也沒有耦合在同一個工廠類裏,這也是工廠方法模式對簡單工廠模式解耦的一個體現。工廠方法模式克服了簡單工廠會違背開 - 閉原則的缺點,又保持了封裝對象創建過程的優點。
但工廠方法模式的缺點是每增加一個產品類,就需要增加一個對應的工廠類,增加了額外的開發量。
三、抽象工廠模式
抽象工廠模式是所有形態的工廠模式中最爲抽象的。抽象工廠模式可以向客戶端提供一個接口,使得客戶端在不必指定產品的具體類型(不是指實現類)的情況下,能夠創建多個產品族的產品對象。
抽象工廠模式用於產品族,例如高端車和低端車
【發動機接口及實現】
public interface Engine {
void run();
}
class HighEndEngine implements Engine {
@Override
public void run() {
System.out.println("高端發動機,啓動快");
}
}
class LowEndEngine implements Engine {
@Override
public void run() {
System.out.println("低端發動機,啓動慢");
}
}
【輪胎接口及實現】
public interface Tyre {
void use();
}
class HighEndTyre implements Tyre {
@Override
public void use() {
System.out.println("高端輪胎,耐磨防滑");
}
}
class LowEndTyre implements Tyre {
@Override
public void use() {
System.out.println("低端輪胎,磨損大,易打滑");
}
}
【座椅接口及實現】
public interface Seat {
void feel();
}
class HighEndSeat implements Seat {
@Override
public void feel() {
System.out.println("高端座椅,坐着舒適");
}
}
class LowEndSeat implements Seat {
@Override
public void feel() {
System.out.println("低端座椅,坐舊難受");
}
}
1.使用工廠模式
public interface EngineFactory {
Engine createEngine();
}
public interface TyreFactory {
Tyre createTyre();
}
public interface SeatFactory{
Seat createSeat();
}
...
public class Client {
public static void main(String[] args) {
EngineFactory ef = new HighEndEngineFactory();
Engine engine = ef.createEngine();
TyreFactory tf = new HighEndTyreFactory();
Tyre tyre = tf.createTyre();
SeatFactory sf = new HighEndEngineSeatFactory();
Seat seat = sf.createSeat();
}
}
如果需要拼裝一輛高端的轎車,客戶端需要創建 3 個高端的工廠獲取發動機、輪胎和座椅才能完成需求。這樣客戶端就與多個類進行耦合,違反了迪米特法則。假設,高端產品族新增一個安全鎖的產品,我們又要新建相應的抽象工廠和子類工廠,項目結構的複雜度大大增加。因此,簡單工廠模式和工廠方法模式只適用於創建某一類對象,對於產品族維度劃分的產品創建的操作就顯得繁瑣了。
2.使用抽象工廠模式
【工廠接口】
public interface CarFactory {
Engine createEngine();
Tyre createTyre();
Seat createSeat();
}
【工廠實現類】
public class HighEndCarFactory implements CarFactory {
@Override
public Engine createEngine() {
return new HighEndEngine();
}
@Override
public Tyre createTyre() {
return new HighEndTyre();
}
@Override
public Seat createSeat() {
return new HighEndSeat();
}
}
public class LowEndCarFactory implements CarFactory {
@Override
public Engine createEngine() {
return new LowEndEngine();
}
@Override
public Tyre createTyre() {
return new LowEndTyre();
}
@Override
public Seat createSeat() {
return new LowEndSeat();
}
}
【客戶端調用】
public class Client {
public static void main(String[] args) {
CarFactory cf = new HighEndCarFactory();
Engine engine = cf.createEngine();
Tyre tyre = cf.createTyre();
Seat seat = cf.createSeat();
}
}
這種實現方式對應上文的內容:客戶端在不必指定產品的具體類型(發動機、輪胎、座椅)的情況下,能夠創建多個產品族的產品對象。當產品族中新增幾個產品,只需創建產品類以及在對應的工廠類中添加對應的方法即可。
優點:
-
抽象工廠模式最大的好處是易於交換產品系列,由於具體工廠類,在一個應用中只需要在初始化的時候出現一次,這就使得改變一個應用的具體工廠變得非常容易,它只需要改變具體工廠即可使用不同的產品配置。不管是任何人的設計都無法去完全防止需求的更改,或者項目的維護,那麼我們的理想便是讓改動變得最小、最容易。
-
抽象工廠模式的另一個好處就是它讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操作實例,產品實現類的具體類名也被具體的工廠實現類分離,不會出現在客戶端代碼中。