面試官:小夥子,夠了夠了,一個工廠模式你都在這說半個小時了!

前程有光發表於2020-12-27

前言

建立型模式、主要用於解決Java物件的建立問題

工廠模式

工廠模式的說明

在物件導向的程式設計中,繼承和多型的概念實現了父類與子類之間的(is-A)關係

基於這種關係實現了子類的多型性,這種抽象的方式為子類提供了相同的操作並且支援子類的功能性擴充。但是出現了這樣的問題?

Verhicle verhicle = new Car();

Verhicle verhicle = new Truck();

problem所在

  1. 子類複寫了父類的方法,那麼子類例項化父類物件時,就必須每一個子類都要進行。這樣就造成了對於不同的子類要呼叫不同的構造器去例項化父類物件。缺乏統一性操作
  2. 另外從上面的兩行程式碼可以看出子類與父類之間的依賴關係、耦合度高
  3. 違反了父類的開閉原則子類的單一職責原則

簡單工廠模式的引入,實現邏輯的封裝,使用公共的工廠類實現統一建立物件例項化父類的行為。

簡單工廠模式

  • 簡單工廠實現的三種方式
    1. 靜態工廠模式
    2. 使用反射機制進行類註冊
    3. 使用newInstance方法進行類註冊

簡單工廠的UML圖

  • 靜態工廠模式的解決方式

建立一個單獨的verhicle簡單工廠類。通過內建列舉儲存所有需要建立例項的子類,並通過統一的create(type)方法根據傳入引數的型別實現按需建立例項。

public class VerhicleFactory {
    public enum VerhicleType {
        Car,Truck,Boat;
    }

    public static Verhicle create(VerhicleType type) {
        if(type.equals(VerhicleType.Car)) {
            return new Car();
        }
        if(type.equals(VerhicleType.Truck)) {
            return new Truck();
        }
        if(type.equals(VerhicleType.Boat)) {
            return new Boat();
        }
        else return null;
    }
}

優勢

這種額外使用工廠類的方式,解決了上面子類例項化父類的破壞單一職責原則、實現了構造例項的統一性操作。

缺點

可以從儲存的列舉看出,一旦新增擴充的子類就必須修改工廠的列舉,破壞了工廠類自身的開閉原則。

仍然沒有解決父類的對內關閉的對外擴充的開閉原則。

  • 使用反射機制進行類註冊的解決方式

為了解決靜態工廠模式破壞自身開閉原則的弊端、我們可以使用反射機制使得註冊的新類在使用時被例項化。從而保證了對外擴充開發,對內修改閉合。也就是說即使新增對父類擴充的子類,也不再重新修改靜態工廠內的列舉。

//服務端

public class Product {/*類體內容*/}
public class Product1 extends Product {}
/*...更多的擴充子類...*/
public class MoreProducts extends Product {}

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

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

/**實現對擴充子類的註冊、所有的擴充子類將會被記錄在Map集合中
 *@parameter
 * productId子類ID也就是子類的型別,對應靜態工廠的列舉型別
 * productClass子類的類物件,也就是子類的Class物件
 */
public void registerProduct(String productId, Class productClass) {
    registeredProduct.put(productId, productClass);
}

/**更具傳入的子類型別、構造對應的子類例項並返回
 *@parameter
 * ProductType子類型別和上面的子類ID一致,對應靜態工廠的列舉型別
 */
public Product createProduct(String ProductType) throws InstantiationException,IllegalAccessException {
    Class productClass = registeredProduct.get(ProductType);
    return (Product) productClass.newInstance();
}

//客戶端就依據相應的方法,進行類的註冊和例項建立

優點

解決了靜態工廠類破壞開閉原則的弊端,把註冊和建立例項分開實現註冊的類在使用時才例項化。

缺點

反射機制影響了程式效能、使用多了的話,程式效能肯定要低效很多。

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

只是基於上面反射機制進行類註冊的思想進行了一個小的修改

  1. 避免使用反射機制、提高效能
  2. 如何實現呢?
    • Map集合中不在儲存Class物件,而是已經建立的子類例項
  • 基類中建立一個抽象方法
  • 子類全部複寫、方法體是建立子類物件
    • 這樣就可以實現上面Map集合儲存的時已經建立的子類例項
//服務端

public abstract class Product {
    Product newInstance();
}
public class Product1 extends Product {}
/*...更多的擴充子類...*/
public class MoreProducts extends Product {
    @override
    public  MoreProducts newInstance() {
        return new MoreProducts();
    }
}

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

private Map<String,Product> registeredProduct = new 
    HashMap<String,Product>();

/**實現對擴充子類的註冊、所有的擴充子類將會被記錄在Map集合中
 *@parameter
 * productId子類ID也就是子類的型別,對應靜態工廠的列舉型別
 * productClass子類的類物件,也就是子類的Class物件
 */
public void registerProduct(String productId, Product productType) {
    registeredProduct.put(productId, productType);
}

/**更具傳入的子類型別、構造對應的子類例項並返回
 *@parameter
 * ProductType子類型別和上面的子類ID一致,對應靜態工廠的列舉型別
 */
public Product createProduct(String ProductId) throws InstantiationException,IllegalAccessException {
    Product productType  = registeredProduct.get(ProductId);
    return (Product) productType.newInstance();
}

//客戶端就依據相應的方法,進行類的註冊和例項建立

優點

把基類之間劃為抽象類,解決了父類的開閉原則和子類的單一職責原則。支援了建立物件的統一性操作。

缺點

類之間的繼承關係造成程式碼依然耦合度高的問題。

仍然存在未實現父類對內修改閉合的風險。

工廠方法模式

工廠方法模式的UML圖

image

解決方式:

  1. 服務端把產品介面化、對於所有需要擴充的產品可以直接實現統一介面方法,以及自定義方法。

  2. 另外抽象化工廠、可以將產品的服務化功能整合在抽象工廠,並且內建構造例項的抽象方法。那麼所有的擴充產品的工廠類都可以繼承實現對應的構造方法和功能。

//  sever
public interface Product{
    void features();      //定義統一的功能
}
public class Product1 implements Product {/*實現統一介面方法、以及自定義方法*/}
public class MoreProducts implements Product {/*實現統一介面方法、以及自定義方法*/}

/*工廠方法*/

public abstract class AbastractFactory {
    Product create();        //定義統一構造器
    void sever(){/*相應的統一服務功能*/}
}
public class Product1Factory implements AbstractFactory {/*實現構造器*/}
public class MoreProductsFactory extends AbstractFactory {/*實現構造器*/}

優勢:

將產品解耦,並且每一個擴充產品都有相應的工廠類實現對應的構造例項以及服務功能

缺點:

易造成工廠的冗餘、擴充產品過多時出現工廠類增多。

抽象工廠模式

解決方法:

在工廠方法模式的基礎之上、在抽象工廠中不再單獨建立統一的構造器,而是建立一類或者所有的擴充產品構造器。這樣工廠類繼承的時候只需要少了的繼承就可以完成多個擴充產品的構造任務。

//  sever
public interface Product{
    void features();     //定義統一的功能
}
public class Product1 implements Product {/*實現統一介面方法、以及自定義方法*/}
public class MoreProducts implements Product {/*實現統一介面方法、以及自定義方法*/}

/*抽象工廠模式*/

public abstract class AbastractFactory {
    Product createProduct1();        //定義Product1構造器
    Product createMoreProducts();    //定義MoreProducts構造器
    void sever(){/*相應的統一服務功能*/}
}

public class ProductFactory extends AbstractFactory {/*實現對應構造器*/}

優點:

減少了工廠類的數量、避免冗餘問題

最後

歡迎關注公眾號:前程有光,領取一線大廠Java面試題總結+各知識點學習思維導+一份300頁pdf文件的Java核心知識點總結!

相關文章