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

前言

简单工厂模式,工厂方法模式,抽象工厂模式,这三个模式,当然还有单例模式,建造者模式等等,应该是日常工作中常用的,尤其是工厂模式,应该是最最常见的模式,对理解面向对象有重要的实际意义。

简单工厂模式

最简单,最直接,能满足大部分日常需求,不足是工厂类太简单——无法满足开闭原则,对多个产品的扩展不利

工厂方法模式——交给子类去创建

工厂方法模式,有了进步,把工厂类进行改进,提升为一个抽象类(接口),把对具体产品的实现交给对应的具体的子类去做,解耦多个产品之间的业务逻辑。

前面都是针对一个产品族的设计,如果有多个产品族的话,就可以使用抽象工厂模式

抽象工厂模式

抽象工厂模式的工厂,不再维护一个产品等级的某个产品(或说一个产品结构的某个产品更好理解),而是维护产品结构里的所有产品(横向x轴),具体到代码就是多个抽象方法去对应产品等级结构的各个产品实例

具体的工厂类实现抽象工厂接口,去对应各个产品族,每一个具体工厂对一个产品族,获得该产品族的产品结构(所有产品)

抽象工厂模式中的方法对应产品等级结构(每个类型中的具体产品),具体子工厂对应不同的产品族(产品类型)

面试题——计算器

想到一个面试题: 写一个简单的计算器,满足加减乘除运算。逻辑比较简单:接受计算数据的输入,进行计算,返回结果。用Java实现。

面向过程版

面向过程的设计本身没有错,但是如果面试的是 java 的相关职位,使用一门面向对象的语言这样写是非常危险的。因为这样写,谁都会,但凡学过编程的,没有不会的。更重要的问题是,这样写的目的仅仅是为了完成任务,没有任何面向对象的思维体现。实际业务中,类似的程序一旦扩展,这样的代码是没有办法维护的。

缺点:完全面向过程设计,所有逻辑都集中在一个类(方法、函数里),缺少代码的重用……

这里的除 0 异常检测也是考点之一。

public class Main {
    public static void main(String[] args) {
        // 1、先接受数据的输入
        // 2、进行计算
        // 3、返回计算结果
        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();
    }
}

客户端代码

        // 1、计算数据的输入
        // 2、进行计算
        // 3、返回计算结果
        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);
    }
}

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

原文地址:https://www.cnblogs.com/kubixuesheng/p/10353209.html

时间: 2024-10-22 17:22:22

对比总结三个工厂模式(简单工厂,工厂方法,抽象工厂)的相关文章

JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)

在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.但是在一些情况下, new操作符直接生成对象会带来一些问题.举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象. 在这些情况,新对象的建立就是一个 "过程",不仅是一个操作,像一部大机器中的一个齿轮传动. 模式的问题:你如何能轻松方便地构造对象实例,而不必关心构造对象实

工厂模式——简单工厂模式

工厂模式分三类:简单工厂模式.工厂方法模式.抽象工厂模式. 首先介绍比较简单一种工厂模式——简单工厂模式. 我们以计算器作为一个例子来说明工厂模式到底是什么.作为一个几年前大一从C语言起步的初级程序员,到现在写代码的思维还是停留在结构化编程,虽然Java学了有几年了,总是说面向对象面向对象.但是让实现一个计算器的程序,估计大部分人都会以C语言式的结构化编程来写,几个if语句或者一个switch来搞定.我也同样不例外,为了提高自己的编程水平,所以一点点开始学设计模式.其实在很多开源代码里也有很多经

工厂模式,简单工厂模式,抽象工厂模式三者有什么区别

工厂模式,也叫做说虚构造器,在简单工厂中间插入了一个具体产品工厂,这个工厂知道产品构造时候的具体细节,而简单工厂模式的产品具体构造细节是在一个个if/else分支,或者在switch/case分支里面的.工厂模式的好处就在于将工厂和产品之间的耦合降低,将具体产品的构造过程放在了具体工厂类里面.在以后扩展产品的时候方便很多,只需要添加一个工厂类,一个产品类,就能方便的添加产品,而不需要修改原有的代码.而在简单工厂中,如果要增加一个产品,则需要修改工厂类,增加if/else分支,或者增加一个case

工厂模式简单例子

工厂模式: 1 package factorymode; 2 /** 3 * 工厂模式简单例子 4 * @author Administrator 5 * 6 */ 7 public class FactoryDemo { 8 9 public static void main(String[] args) { 10 IFruit fruit = Factory.getFrit("橘子"); 11 if(fruit != null) { 12 System.out.println(fr

设计模式(二)工厂模式:2-工厂方法模式

模拟场景: 继续沿用在简单工厂模式中讨论的,运算器相关的场景. 思想: 考虑之前最初的设计,简单工厂模式中,最大的问题在于,面对新增的需要在工厂中创建的对象,对其的修改会违反开闭原则. 工厂方法模式(Factory Method)对于这种问题的解决方案是:将生产运算器的工厂抽象出来(AbsOperationFactory),然后为原来每一个需要创建的对象(继承AbsOperation),都建立一个专门的工厂.这样一来,可以巧妙地利用多态的性质,完成代码的解耦. 由此可见,工厂方法模式,是模板方法

创建和使用解耦——工厂模式详解(工厂方法+抽象工厂)

1.前言 直接new一个对象是最简单的创建对象的方式,但大量出现在业务代码中会带来至少两个问题.1:创建对象的细节直接暴露在业务代码中,修改实现细节必须修改相关的大量客户端代码.2:直接面向具体类型编程,违反了面向接口编程的原则,系统进行扩展时也不得不进行大量修改.要使得系统具有的良好的可扩展性以及后期易于维护,必须实现对产品的获取和对产品的使用解耦.要做到这两点,首先要对客户端代码屏蔽掉创建产品的细节,其次,客户端必须面向产品的抽象编程,利用java的多态特性在运行时才确定具体的产品.而这,正

总结工厂模式---简单工厂、工厂方法、抽象工厂

简单工厂 首先,我们先看一个简单工厂的例子: #coding=utf-8 class Mercedes(object): """梅赛德斯 """ def __repr__(self): return "Mercedes-Benz" class BMW(object): """宝马 """ def __repr__(self): return "BMW&quo

设计模式之工厂模式(简单工厂,工厂方法,抽象工厂)

设计模式6大原则:1.开闭原则:对拓展开放,对修改关闭2.单一职责原则:一个类只复杂一项职责3.里氏原则:子类可以扩展父类的功能,但不改变父类原有的功能4.依赖倒置原则:面向接口编程5.接口隔离原则:设计接口功能尽量细粒度,最小功能单元6.迪米特法则:降低耦合度(局部变量中,不要引入新的类) 这里对工厂模式做一个学习记录 这里用简单计算器来举例. 很简单,我们需要3个输入: a1 代表第一个数字 a2 代表第二数字 operator 代表运算符 这三个参数输入后得到一个输出result 1.平时

设计模式之工厂模式-简单工厂(02)

设计模式分创建型.行为型.结构型: 工厂模式属于创建型模式,分(1)简单工厂(静态工厂)(2)工厂方法(3)抽象工厂,下面分别通过代码来介绍(本篇文章北风网的视频看后写的): (一)简单工厂 简单工厂描述:通过专门定义的一个类来创建其他类的实例,被创建的实例通常都具有共同的父类: 简单工厂的角色及其职责:(1)工厂角色[creator]:该模式的核心,负责创建所有实例的内部逻辑,工厂类可以被外界直接调用,创建所需要的产品实例(2)抽象角色[Product]:简单工厂模式 所创建的所有对象的父类,

详解设计模式之工厂模式(简单工厂+工厂方法+抽象工厂)

园子里关于23种设计模式的博文已经可以说是成千上万.车载斗量.屯街塞巷.不计其数.数不胜数.摩肩接踵.汗牛充栋.车水马龙.门庭若市.琳琅满目直至让人眼花缭乱了.在这样的大环境下之所以来写设计模式类的博文,并不是像一些"非主流"的爱情观那样"宁缺毋滥". 只是其一呢,因为相当于给自己做一个总结,加深一下自己这方面的认识,因为掌握了和把它写出来我感觉后者还可以对技能有一个提升,其二呢是因为最近公司有一个内部的training需要讲设计模式. v写在前面 在这里呢,需要向