【設計模式】——徹底搞懂三工廠模式

        最近在學習Spring的源碼,雖然對源碼認識不深刻,卻勾起了對設計模式的熱愛,越發能理解,這些理論的東西爲什麼值得被稱讚。設計模式本身系統的學習過三遍了,但依舊停留在記不住的情況下,這次的總結源於Spring源碼的學習,自認爲有了一定的認知,如果存在理解偏頗之處,懇請各位大神斧正,小妹不勝感激。

         三工廠模式最重要的認知,屬於創建型。這意味着簡單工廠,工廠模式,抽象工廠存在的意義是爲了創建對象。在Java中稱爲Object,如果放到Spring中則稱爲創建bean。其實object等同於bean的概念,只不過Spring中有對bean的管理,即BeanFacotry。由此可見Spring的核心還是利用了工廠模式。學會三工廠更利於對Spring源碼的學習。

下面舉個例子,詳細介紹下三個模式的不同之處

編寫具有加減乘除運算方式的計算器,最簡單的業務代碼

public abstract class Operation {
    private double numberA=0;
    private double numberB=0;

    public double getNumberA() {
        return numberA;
    }

    public void setNumberA(double numberA) {
        this.numberA = numberA;
    }

    public double getNumberB() {
        return numberB;
    }

    public void setNumberB(double numberB) {
        this.numberB = numberB;
    }

    public abstract double getResult();
}

/**
 * 加法
 */
public class OperationAdd extends Operation {
    @Override
    public double getResult() {
        double result = 0;
        result = getNumberA()+getNumberB();
        return result;
    }
}

/**
 * 減法
 */
public class OperationSub extends Operation {
    @Override
    public double getResult() {
        double result = 0;
        result = getNumberA()-getNumberB();
        return result;
    }
}


/**
 * 乘法
 */
public class OperationMul extends Operation {

    @Override
    public double getResult() {
        double result = 0;
        result = getNumberA()*getNumberB();
        return result;
    }
}


/**
 * 除法
 */
public class OperationDiv extends Operation {
    @Override
    public double getResult() {
        double result = 0;
        result = getNumberA()/getNumberB();
        return result;
    }
}

簡單工廠——用一個單獨的類來做實例對象的創建

/**
 * 運算工廠
 */
public class OperationFactory {
    public static Operation createOperate(String operate) {
        Operation operation = null;

        switch (operate) {
            case "+":
                operation = new OperationAdd();
                break;
            case "-":
                operation = new OperationSub();
                break;
            case "*":
                operation = new OperationMul();
                break;
            case "/":
                operation = new OperationDiv();
                break;
        }
        return operation;
    }
}

客戶端執行邏輯

public class Client {
    public static void main(String[] args) {
        /**
         * 簡單工廠的核心,對象的創建本質放到服務端的,客戶端的作用只是決定使用具體邏輯處理方式
         */
        Operation operate = OperationFactory.createOperate("+");
        operate.setNumberA(1);
        operate.setNumberB(2);
        double result = operate.getResult();
        System.out.println(result);
    }
}

        簡單工廠模式最大的優點在於工廠類中包含了必要的邏輯判斷,根據客戶端的條件動態實例化相關的類,對於客戶端來說,去除了與具體產品的依賴。

         但是簡單工廠並不能成爲一個完整的設計模式,是因爲它並不符合開閉原則,例如 再加一個開根號,運算工廠就需要被修改。因此在產生了工廠模式,遵循開閉原則,將程序設計更安心。

大家要在簡單工廠理解一個點,運算類本質上是由運算工廠創建的(服務端,非客戶行爲創建),客戶端只是決定具體的使用邏輯,也就造成無法遵循開閉原則

依舊使用上面的例子,假設此增加一個求A的B次方,用簡單工廠方式,就是增加一個Operation的子類,並且修改OperationFactory類即可。但是使用工廠模式該怎麼實現呢?

/**
 * A的B次方
 */
public class OperationPower extends Operation {
    @Override
    public double getResult() {
        double result = 1;
        int B = Double.valueOf(getNumberB()).intValue();
        for (int i = 0; i < B; i++) {
            result= result*getNumberA();
        }
        return result;
    }
}

工廠模式 ,定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類中。

/**
 * 定義一個創建對象的接口,讓子類實現接口,讓類的實例化延遲早子類實現中
 */
public interface IFacotry {

    /**
     * 定義創建類的方法
     * @return
     */
    Operation createOperation();
}

延遲到子類中的創建

/**
 * 創建A的B次方的工廠
 */
public class OparetionPowerFactory implements IFacotry {
    @Override
    public Operation createOperation() {
        return new OperationPower();
    }
}

客戶端的使用方式

 public static void main(String[] args) {
        /**
         * 客戶決定邏輯,自行使用對應的工廠
         */
        OparetionPowerFactory factory = new OparetionPowerFactory();
        /**
         * 通過工廠,將類的實現延遲到子類工廠中,新增其他運算邏輯,則不需要額外修改服務端代碼,新增即可
         */
        Operation operation = factory.createOperation();
        operation.setNumberA(2);
        operation.setNumberB(3);
        double result = operation.getResult();
        System.out.println(result);
    }

再解釋一遍:工廠方法實現是,客戶端需要決定實例化哪一個工廠來實現運算類,選擇判斷的問題還是存在的,也就是說工廠方法把簡單工廠的內部邏輯判斷移到了客戶端代碼來進行,如果你想要加功能,本來是改工廠類,而現在是修改客戶端。 

 

工廠模式解決了一條生產線的問題,但是如果同時有多個生產線生產同樣的內容,那麼又該如何處理呢?

通常常見的案例就是換數據庫問題,使用mysql需要有於表對應的user類,school類,那麼換成oracle同樣有這訴求。此時我們不可能每換一種數據庫就新增一組邏輯上的接口吧。而是希望所有複雜的創建流程,全部由程序創建處理,這樣廣大同胞再也不用加班了!

被動創建對象的方式,最常見的就是反射,反射通過全類名加載,可以實現自動創建類。

抽象工廠模式,提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

先使用工廠方式來實現下,上述案例 關於User的兩種數據庫改造

public class User {
}


/**
 * user 接口
 */
public interface IUser {
    void insert(User user);

    User getUser(int id);
}

/**
 * 定義一個創建對象的接口,讓子類實現接口,讓類的實例化延遲早子類實現中
 */
public interface IFacotry {

    /**
     * 定義創建類的方法
     * @return
     */
    IUser createUser();

}

public class MysqlUser implements IUser {
    @Override
    public void insert(User user) {
        System.out.println("mysql create user");
    }

    @Override
    public User getUser(int id) {
        System.out.println("mysql query user");
        return null;
    }
}


public class OracleUser implements IUser {
    @Override
    public void insert(User user) {
        System.out.println("oracle create user");
    }

    @Override
    public User getUser(int id) {
        System.out.println("oracle query user");
        return null;
    }
}

public static void main(String[] args) {
        /**
         * 客戶決定邏輯,自行使用對應的工廠
         */
        MysqlFacotry factory = new MysqlFacotry();
        IUser user = factory.createUser();

        user.insert(new User());

        user.getUser(1);
}

如果此時在新增一個school類呢?那麼在新增一種數據庫方式呢?簡直是噩夢。

編程是門藝術,這樣大批量的改動,不是很明智的選擇。

用反射+配置文件的方式改造它。

public class DataAccess {

    private final static String path = "com.mandy.factory.service";
    private String db;

    public DataAccess(String db) {
        this.db = db;
    }
    
    public  IUser createUser() {
//        com.mandy.factory.service.MysqlUser
        String classPath = path +"."+ db + "User";
        Class<?> clazz = null;
        try {
            clazz = Class.forName(classPath);
           return (IUser) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class Client {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String db = scanner.nextLine();

        DataAccess dataAccess = new DataAccess(db);

        IUser user = dataAccess.createUser();
        user.getUser(1);
    }
}

        到此爲止一個object的創建可以通過讀取配置+反射的方式創建,其實從抽象工廠模式就已經可以看到Spring最初的設計了,解決不斷去new的方式,將手動的new改爲由Spring去創建,創建的方式本質上同樣是使用的反射。

        個人理解 如果說在Java開發過程真正的巨人的是什麼,相比各種各樣偉大的框架,我堅信最偉大的基石就是這些設計模式。

         不想做架構師的技術負責人不是好的開發。未來要選擇的道路都是聯通,所以要學的還有很多!

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