对比总结三个工厂模式(简单工厂,工厂方法,抽象工厂)

前言

简单工厂模式,工厂方法模式,抽象工厂模式,这三个模式,固然还有单例模式,建造者模式等等,应该是平常工做中经常使用的,尤为是工厂模式,应该是最最多见的模式,对理解面向对象有重要的实际意义。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);
    }
}

到这里也差很少了,虽然还有不少问题……关键是思想的掌握 

相关文章
相关标签/搜索