對比總結三個工廠模式(簡單工廠,工廠方法,抽象工廠)

前言

簡單工廠模式,工廠方法模式,抽象工廠模式,這三個模式,固然還有單例模式,建造者模式等等,應該是平常工做中經常使用的,尤爲是工廠模式,應該是最最多見的模式,對理解面向對象有重要的實際意義。java

簡單工廠模式

最簡單,最直接,能知足大部分平常需求,不足是工廠類太簡單——沒法知足開閉原則,對多個產品的擴展不利面試

工廠方法模式——交給子類去建立

工廠方法模式,有了進步,把工廠類進行改進,提高爲一個抽象類(接口),把對具體產品的實現交給對應的具體的子類去作,解耦多個產品之間的業務邏輯。編程

前面都是針對一個產品族的設計,若是有多個產品族的話,就可使用抽象工廠模式設計模式

抽象工廠模式

抽象工廠模式的工廠,再也不維護一個產品等級的某個產品(或說一個產品結構的某個產品更好理解),而是維護產品結構裏的全部產品(橫向x軸),具體到代碼就是多個抽象方法去對應產品等級結構的各個產品實例框架

具體的工廠類實現抽象工廠接口,去對應各個產品族,每個具體工廠對一個產品族,得到該產品族的產品結構(全部產品)ide

抽象工廠模式中的方法對應產品等級結構(每一個類型中的具體產品),具體子工廠對應不一樣的產品族(產品類型)函數

面試題——計算器

 想到一個面試題: 寫一個簡單的計算器,知足加減乘除運算。邏輯比較簡單:接受計算數據的輸入,進行計算,返回結果。用Java實現。this

面向過程版

面向過程的設計自己沒有錯,可是若是面試的是 java 的相關職位,使用一門面向對象的語言這樣寫是很是危險的。由於這樣寫,誰都會,但凡學過編程的,沒有不會的。更重要的問題是,這樣寫的目的僅僅是爲了完成任務,沒有任何面向對象的思惟體現。實際業務中,相似的程序一旦擴展,這樣的代碼是沒有辦法維護的。spa

缺點:徹底面向過程設計,全部邏輯都集中在一個類(方法、函數裏),缺乏代碼的重用……設計

這裏的除 0 異常檢測也是考點之一。

public class Main {
    public static void main(String[] args) {
        // 一、先接受數據的輸入
        // 二、進行計算
        // 三、返回計算結果
        System.out.println("******計算器********\n 請輸入第一個數:");
        Scanner scanner = new Scanner(System.in);
        String num1 = scanner.nextLine();

        System.out.println("請輸入運算符:");
        String operation = scanner.nextLine();

        System.out.println("請輸入第二個數:");
        String num2 = scanner.nextLine();

        System.out.println("開始計算。。。。。。");
        double result = 0;

        if ("+".equals(operation)) {
            result = Double.parseDouble(num1) + Double.parseDouble(num2);
        } else if ("-".equals(operation)) {
            result = Double.parseDouble(num1) - Double.parseDouble(num2);
        } else if ("*".equals(operation)) {
            result = Double.parseDouble(num1) * Double.parseDouble(num2);
        } else if ("/".equals(operation)) {
            if (Double.parseDouble(num2) != 0) {
                result = Double.parseDouble(num1) / Double.parseDouble(num2);
            } else {
                System.out.println("除數不能爲0!");

                return;
            }
        }

        System.out.println(num1 + operation + num2 + " = " + result);
    }
}

簡單面向對象版

面向對象的設計就是把各個操做抽象爲一個個的類,加法類,減法類,乘法類,除法類……每一個運算類的職責就是進行屬於本身的運算符的計算,客戶端去調用對應的運算類便可。

代碼以下:

public abstract class Operation {
    private double num1;
    private double num2;

    public double getNum1() {
        return num1;
    }

    public void setNum1(double num1) {
        this.num1 = num1;
    }

    public double getNum2() {
        return num2;
    }

    public void setNum2(double num2) {
        this.num2 = num2;
    }

    public abstract double getResult();
}

具體的運算符子類,只用加法舉例,其餘省略。

public class Add extends Operation {
    @Override
    public double getResult() {
        return this.getNum1() + this.getNum2();
    }
}

客戶端代碼

        // 一、計算數據的輸入
        // 二、進行計算
        // 三、返回計算結果
        System.out.println("******計算器********\n 請輸入第一個數:");
        Scanner scanner = new Scanner(System.in);
        String num1 = scanner.nextLine();

        System.out.println("請輸入運算符:");
        String operation = scanner.nextLine();

        System.out.println("請輸入第二個數:");
        String num2 = scanner.nextLine();

        System.out.println("開始計算。。。。。。");
        double result = 0;
        // 類型轉換
        double a = Double.parseDouble(num1);
        double b = Double.parseDouble(num2);

        if ("+".equals(operation)) {
            Operation o = new Add();
            o.setNum1(a);
            o.setNum2(b);
            result = o.getResult();
        }

寫到這裏,貌似比以前也沒什麼大的改變,只是使用了面向對象的一丟丟,使用了類……在客戶端仍是須要顯式的去 new 對應的運算類對象進行計算,客戶端裏仍是維護了大量的業務邏輯……

繼續改進,使用工廠模式——簡單工廠模式

簡單工廠模式版

通常學過的人,會當即想到該模式

public abstract class Operation {
    private double num1;
    private double num2;

    public double getNum1() {
        return num1;
    }

    public void setNum1(double num1) {
        this.num1 = num1;
    }

    public double getNum2() {
        return num2;
    }

    public void setNum2(double num2) {
        this.num2 = num2;
    }

    public abstract double getResult();
}

public class Add extends Operation {
    @Override
    public double getResult() {
        return this.getNum1() + this.getNum2();
    }
}

public class Sub extends Operation {
    @Override
    public double getResult() {
        return this.getNum1() - this.getNum2();
    }
}
 
///////////////  簡單工廠類(也可使用反射機制)
public class OpreationFactory {
    public static Operation getOperation(String operation) {
        if ("+".equals(operation)) {
            return new Add();
        } else if ("-".equals(operation)) {
            return new Sub();
        }

        return null;
    }
}
 
////////////// 調用者(客戶端)
public class Main {
    public static void main(String[] args) {
        System.out.println("******計算器********\n請輸入第一個數:");
        Scanner scanner = new Scanner(System.in);
        String num1 = scanner.nextLine();

        System.out.println("請輸入運算符:");
        String operation = scanner.nextLine();

        System.out.println("請輸入第二個數:");
        String num2 = scanner.nextLine();

        System.out.println("開始計算。。。。。。");
        double result = 0;
        // 類型轉換
        double a = Double.parseDouble(num1);
        double b = Double.parseDouble(num2);

        Operation oper = OpreationFactory.getOperation(operation);
        // TODO 有空指針異常隱患
        oper.setNum1(a);
        oper.setNum2(b);
        result = oper.getResult();
        System.out.println(num1 + operation + num2 + " = " + result);
    }
}

這樣寫,客戶端(調用者)無需反覆修改程序,也不須要關注底層實現,調用者只須要簡單瞭解或者指定一個工廠的接口,而後去調用便可一勞永逸,而底層的修改不會影響調用者的代碼結構——實現瞭解耦。且每一個操做符類都各司其職,單一職責,看着還能夠

可是這時候面試官說了,給我增長開平方運算,回想以前的簡單工廠設計模式,每次增長新的產品都須要去修改原來的工廠代碼——這樣不符合OCP,那麼天然想到了工廠方法模式

工廠方法模式版

只舉個加法的例子得了

// 將工廠,又抽象了一層
public interface OpreationFactory {
    Operation getOperation();
}

////// 具體工廠類,生產不一樣的產品,好比加減乘除,開平方等
public class AddFactory implements OpreationFactory {
    @Override
    public Operation getOperation() {
        return new Add();
    }
}
 
///// 抽象的產品實體類
public abstract class Operation {
    private double num1;
    private double num2;

    public double getNum1() {
        return num1;
    }

    public void setNum1(double num1) {
        this.num1 = num1;
    }

    public double getNum2() {
        return num2;
    }

    public void setNum2(double num2) {
        this.num2 = num2;
    }

    public abstract double getResult();
}

public class Add extends Operation {
    @Override
    public double getResult() {
        return this.getNum1() + this.getNum2();
    }
}
 
/////////// 客戶端
public class Main {
    public static void main(String[] args) {
        System.out.println("******計算器********\n 請輸入第一個數:");
        Scanner scanner = new Scanner(System.in);
        String num1 = scanner.nextLine();

        System.out.println("請輸入運算符:");
        String operation = scanner.nextLine();

        System.out.println("請輸入第二個數:");
        String num2 = scanner.nextLine();

        System.out.println("開始計算。。。。。。");
        double result = 0;
        // 類型轉換
        double a = Double.parseDouble(num1);
        double b = Double.parseDouble(num2);

        // TODO 這裏又須要判斷了
        if ("+".equals(operation)) {
            // 獲得加法工廠
            OpreationFactory opreationFactory = new AddFactory();
            // 計算 +
            Operation oper = opreationFactory.getOperation();
            oper.setNum1(a);
            oper.setNum2(b);
            result = oper.getResult();
        }

        // ......
        System.out.println(num1 + operation + num2 + " = " + result);
    }
}

工廠方法模式雖然避免了每次擴展運算符的時候,都修改工廠類,可是把判斷的業務邏輯放到了客戶端裏,各有缺點吧……不要爲了面向對象而面向對象。

改進的工廠方法模式版

不過,仍是能夠改進的……使用反射動態加載類 + 配置文件/註解 等等,且對重複代碼進行提煉和封裝……其實框架就這麼一步步來的。

代碼以下(+和-):

public interface OpreationFactory {
    Operation getOperation();
}

public class AddFactory implements OpreationFactory {
    @Override
    public Operation getOperation() {
        return new Add();
    }
}

public class SubFactory implements OpreationFactory {
    @Override
    public Operation getOperation() {
        return new Sub();
    }
}

public abstract class Operation {
    private double num1;
    private double num2;

    public double getNum1() {
        return num1;
    }

    public void setNum1(double num1) {
        this.num1 = num1;
    }

    public double getNum2() {
        return num2;
    }

    public void setNum2(double num2) {
        this.num2 = num2;
    }

    public abstract double getResult();
}

public class Add extends Operation {
    @Override
    public double getResult() {
        return this.getNum1() + this.getNum2();
    }
}

public class Sub extends Operation {
    @Override
    public double getResult() {
        return this.getNum1() - this.getNum2();
    }
}

////////////// 封裝了一些操做
public enum  Util {
    MAP {
        // TODO 寫在配置文件裏
        @Override
        public Map<String, String> getMap() {
            Map<String, String> hashMap = new HashMap<>();
            hashMap.put("+", "compute.object.AddFactory");
            hashMap.put("-", "compute.object.SubFactory");

            return hashMap;
        }

        public double compute(double a, double b, OpreationFactory opreationFactory) {
            Operation oper = opreationFactory.getOperation();
            oper.setNum1(a);
            oper.setNum2(b);

            return oper.getResult();
        }
    };

    public abstract Map<String, String> getMap();
    public abstract double compute(double a, double b, OpreationFactory opreationFactory);
}

//////////// 客戶端
public class Main {
    private static double result = 0;
    private static double a;
    private static double b;

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        System.out.println("******計算器********\n 請輸入第一個數:");
        Scanner scanner = new Scanner(System.in);
        String num1 = scanner.nextLine();
        System.out.println("請輸入運算符:");
        String operation = scanner.nextLine();
        System.out.println("請輸入第二個數:");
        String num2 = scanner.nextLine();
        System.out.println("開始計算。。。。。。");
        a = Double.parseDouble(num1);
        b = Double.parseDouble(num2);
        Class clazz = Class.forName(MAP.getMap().get(operation));
        result = MAP.compute(a, b, (OpreationFactory) clazz.newInstance());
        System.out.println(num1 + operation + num2 + " = " + result);
    }
}

到這裏也差很少了,雖然還有不少問題……關鍵是思想的掌握 

相關文章
相關標籤/搜索