簡單工廠設計模式&lambda重構簡單工廠模式

概念以及背景
簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它可以根據參數的不同返回不同類的實例,被創建的實例通常都具有共同的父類。
因爲在簡單工廠模式中用於創建實例的方法是靜態(static)方法,因此簡單工廠模式又被稱爲靜態工廠方法(Static Factory Method)模式,它屬於類創建型模式。
工廠模式是最常用的一類創建型設計模式,通常我們所說的工廠模式是指工廠方法模式而簡單工廠模式並不屬於GoF 23個經典設計模式,但通常將它作爲學習其他工廠模式的基礎。

簡單地說就是需要什麼對象,傳入給定格式的參數,就可以獲取所需對象,而調用方無需知道其中創建對象的細節,在創建對象那一方也有自己的實現細節,代碼結構清晰、解耦。

本文涉及源碼在這裏

裏面用三種不同的方式應用了簡單工廠模式,有用到Java8 Lambda 方式重構簡單工廠模式,也有一個我實際開發中應用到的方式。

一、簡單工廠模式

UML類圖如下,有一個抽象金融產品類AbstractFinanceProduct,有三個子類,貸款(LoanFinanceProduct)、股票(StockFinanceProduct)和債券(BondFinanceProduct)都是抽象金融產品類(AbstractFinanceProduct)的子類。
工廠類是SimpleFactory,負責建對象,根據傳入的參數,返回需要的對象。


/**
 * 抽象金融產品類
 */
public abstract class AbstractFinanceProduct {

    /**
     * 所有產品類的公共業務方法
     */
    public void methodSame() {
        String className = this.getClass().getName();
        //公共方法的實現
        System.out.println("抽象類公共方法,該類是:" + className);
    }

    /**
     * 聲明抽象業務方法
     */
    public abstract void disPlayProduct();
}

產品名稱枚舉,用於工廠類創建對象方法的參數,用枚舉的話比用常量好,常量的話方法參數調用方可能會給超出產品範圍的常量,而用枚舉的話產品枚舉類範圍都是明確的,也不用擔心寫錯,如把股票Stock寫成Stick等錯誤寫法。

/**
 * 產品名稱枚舉
 */
public enum ProductEnum {
    /**
     * 產品類別
     */
    Bond("Bond"), Loan("Loan"), Stock("Stock");

    String name;

    ProductEnum(String name) {
        this.name = name;
    }
}
/**
 * 簡單工廠類
 */
class SimpleFactory {

    static AbstractFinanceProduct creatProduct(ProductEnum productEnum) {
        switch (productEnum) {
            case Bond :
                return new BondFinanceProduct();
            case Loan :
                return new LoanFinanceProduct();
            case Stock :
                return new StockFinanceProduct();
            default:
                throw new RuntimeException("No such product" + productEnum.name());
        }
    }
}

工廠類的調用方,只需傳入某個產品的類別,即可返回該產品對象。

public class SimpleFactoryPattern {
    public static void main(String[] args) {
        AbstractFinanceProduct stockProduct = SimpleFactory.creatProduct(ProductEnum.Stock);
        AbstractFinanceProduct bondProduct = SimpleFactory.creatProduct(ProductEnum.Bond);
        stockProduct.methodSame();
        stockProduct.disPlayProduct();
        bondProduct.disPlayProduct();
    }
}
二、用Java8 Lambda重構簡單工廠模式

跟步驟一的UML類圖一樣,如下:


用lambda的方式實現簡單工廠模式,lambda給我們提供了一些常用的函數式接口可以用,簡單工廠模式需要建對象和返回對象,故可用Supplier<T>函數式接口,至於創建所需對象的入參,接着下面看。

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

這裏把所需創建對象的入參做成一個Map的key鍵,而Map的value是一個函數式接口,在設值的時候用lambda實現該value的函數式接口,這裏巧妙地用了lambda的方法引用ClassName::new,實現函數式接口Supplier<T>的get()接口方法。

/**
 * 用lambda的方式實現簡單工廠模式
 */
class SimpleFactoryLambda {
    /**
     * Map的value是一個函數式接口,裏面的接口待實現
     */
    private final static Map<ProductEnum, Supplier<AbstractFinanceProduct>> PRODEUC_MAP = new HashMap<>();
    static {
        //這裏put進去的value就是函數式接口的一個實現,lambda表達式的方法引用
        PRODEUC_MAP.put(ProductEnum.Bond, BondFinanceProduct::new);
        PRODEUC_MAP.put(ProductEnum.Stock, StockFinanceProduct::new);
        PRODEUC_MAP.put(ProductEnum.Loan, LoanFinanceProduct::new);
    }

    static AbstractFinanceProduct creatProduct(ProductEnum productEnum) {
        Supplier<AbstractFinanceProduct> productSupplier = PRODEUC_MAP.get(productEnum);
        if (productSupplier != null) {
            //這裏每次調用都生成新的對象,map的value得到的是一個函數式接口的lambda實現,每次都new新對象出來。
            return productSupplier.get();
        }
        throw new IllegalArgumentException("No such product" + productEnum.name());
    }
}
三、開發中的應用簡單工廠模式的例子

如果不想所創建的對象都是子類繼承父類,高度耦合的(實際也很少這樣),但在實際開發中又需要經常用到創建對象,可以試試下面這種方式。
這個是我在開發中所運用到的,項目需要大量調用淘寶的接口,淘寶請求參數的對象又有不同的屬性,即調用淘寶線上不同的接口,都需要有不同的入參對象DTO,所以這些對象都不是繼承的關係,這裏用簡單工廠模式來建請求淘寶接口的對象。
UML類圖:


/**
 * 請求報文,T 攜帶簡單工廠所創建的不同對象
 * @author Fiuty
 */
public class RequestDTO <T>{
    private T payload;

public class Animal {

    private String name;

    public Animal(String name) {
        this.name = name;
    }

public class People {

    private String name;

    public People(String name) {
        this.name = name;
    }
}

工廠接口類,建對象的方法,統一返回RequestDTO<T>,在泛型中返回所需的對象,而這些對象都不是子類繼承父類,高度耦合。

public interface SimpleFactoryAdvanceService {

    RequestDTO<People> createPeople(String name);

    RequestDTO<Animal> createAnimal(String name);
}

工廠實現類

public class SimpleFactoryAdvanceServiceImpl implements SimpleFactoryAdvanceService {

    @Override
    public RequestDTO<People> createPeople(String name) {
        return new RequestDTO<>(new People(name));
    }

    @Override
    public RequestDTO<Animal> createAnimal(String name) {
        return new RequestDTO<>(new Animal(name));
    }
}

最後調用工廠實現類來生產我們所需的對象:

public class SimpleFactoryPatternAdvance {

    public static void main(String[] agrs) {
        SimpleFactoryAdvanceServiceImpl factory = new SimpleFactoryAdvanceServiceImpl();
        RequestDTO<Animal> animalRequest = factory.createAnimal("特朗普");
        RequestDTO<People> peopleRequest = factory.createPeople("上帝");
        System.out.println("動物的名字是:"+animalRequest.getPayload().getName());
        System.out.println("人類的名字是:"+peopleRequest.getPayload().getName());
    }
}

參考資料:
https://blog.csdn.net/LoveLion/article/details/17517213#commentBox
《Java8 實戰》

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