大话设计模式—工厂模式

来源:互联网 发布:uc davis 人工智能 编辑:程序博客网 时间:2024/05/16 00:57

一、简介

工厂模式主要是为创建对象提供了接口。工厂模式按照《Java与模式》中的提法分为三类:

  1. 简单工厂模式(Simple Factory)
  2. 工厂方法模式(Factory Method)
  3. 抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。还有一种分类法,就是将简单工厂模式看为工厂方法模式的一种特例,两个归为一类。下面是使用工厂模式的两种情况:

1.在编码时不能预见需要创建哪种类的实例。
2.系统不应依赖于产品类实例如何被创建、组合和表达的细节 。

二、简单工厂模式(Simple Factory)

顾名思义,这个模式本身很简单,而且使用在业务较简单的情况下。
它由三种角色组成(关系见下面的类图):

1、工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。

2、抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。

3、具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

类图如下:

这里写图片描述

使用简单工厂模式来实现一个实例:

用面向对象语言实现一个计算机控制台程序:(以java为例)

UML类图如下:
这里写图片描述

源代码如下:

Operation.java

package com.caculate;public class Operation {    protected double numberA=0;    protected 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 double getResult(){        double result=0;        return result;    }}

OperationAdd.java(加法类)

package com.caculate;/** *加法类 */public class OperationAdd extends Operation{    public double getResult() {        double result=0;        result=numberA + numberB;        return result;    }}

OperationSub.java(减法类)

package com.caculate;/** *减法类 */public class OperationSub extends Operation{    public double getResult() {        double result=0;        result=numberA - numberB;        return result;    }}

OperationMul.java(乘法类)

package com.caculate;/** *乘法类 */public class OperationMul extends Operation{    public double getResult() {        double result = 0;        result=numberA * numberB;        return result;    }}

OperationDiv.java(除法类)

package com.caculate;/** *除法类 */public class OperationDiv extends Operation{    public double getReault() {        double result=0;        result=numberA/numberB;        return result;    }}

OperationFactory.java(工厂类,用来实例化需要用到的运算类对象)

package com.caculate;/** *构建一个工厂类来实例化对象 */public class OperationFactory {    /**     * @param operate 传入一个操作符     * @return 返回一个对应该操作数的Operation类对象     */    public static Operation createOperate(String operate){        Operation oper=null;        int n=0;        if (operate.equals("+")) {            n=1;        }        if (operate.equals("-")) {            n=2;        }        if (operate.equals("*")) {            n=3;        }        if (operate.equals("、")) {            n=4;        }        /**注意:         * JRE1.7以下的版本中,switch的判断条件必须是一个int型值,也可以是byte、short、char型值;         * 在1.7增加新特性能传string类型         */        switch (n) {        case 1:            oper=new OperationAdd();//如果传入的操作符是+,则实例化一个加法类对象            break;        case 2:            oper=new OperationSub();//如果传入的操作符是-,则实例化一个减法类对象            break;        case 3:            oper=new OperationMul();//如果传入的操作符是*,则实例化一个乘法类对象            break;        case 4:            oper=new OperationDiv();//如果传入的操作符是/,则实例化一个除法类对象            break;        }        return oper;    }}

Test.java(测试类)

package com.caculate;public class Test {    public static void main(String[] args) {        Operation oper=OperationFactory.createOperate("+");        oper.numberA=1;        oper.numberB=4;        double result=oper.getResult();        System.out.println(result);    }}

运行结果: 5.0

总结:

1、当我们需要修改相应的运算时,只需要修改相应的实现类(加减乘除等的实现);当需要增加其他的复杂运算(比如:平方根,立方根等)时,只需要实现相应的运算(既增加运算类的子类),然后增加运算类工厂中的switch分支即可;在使用的时候,只需要输入运算符号,工厂类就能实例化出合适的对象,通过多态,返回父类的方式来实现计算器的结果,而使用者无需了解具体的实现过程,充分契合了面向对象继承、封装和多态的特性。

2、使用了简单工厂模式后,我们的程序更加符合现实中的情况;而且客户端免除了直接创建产品对象的责任,而仅仅负责”消费”产品。

我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以实现起来会很麻烦。所以简单工厂模式适用于业务较简单的情况下。而对于复杂的业务环境可能不太适应。这就应该由工厂方法模式来出场了。

三、工厂方法模式(Factory Method)

简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。但是,如果我们要加一个“求M数的N次方的功能”,我们是一定需要给运算工厂类的方法里加“case”分支条件的,这就修改了原来的类,就等于我们不但对扩展开放了,也对修改开放了,违背了开放-封闭原则

于是,工厂方法模式就来了。

工厂方法模式(Factory Method)定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法模式结构图如下:

这里写图片描述

还是以上面的计算器为例子,既然这个工厂类与分支耦合,那么我们就对它下手,根据依赖倒转原则,我们把工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。然后,所有的要生产具体类的工厂,就去实现这个接口。这样,一个简单工厂模式的工厂类,变成了一个工厂抽象接口和多个具体生成对象的工厂,于是我们增加“求M数的N次方”的功能时,就不需要更改原有的工厂类,只需要增加此功能的运算类和相应的工厂类就可以了。

这里写图片描述

这样,整个工厂和产品体系都没有修改的变化而只是扩展的变化,这就完全符合开放-封闭原则的精神,同时又保持了封装对象创建过程的优点,降低了客户端与产品对象的耦合。

另外,工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂方法的内部逻辑判断移到了客户端代码来进行。如果想要增加功能,本来是改工厂类,而现在是修改客户端。

工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。但还存在不足,就是由于每增加一个产品,就需要增加一个产品工厂的类,增加了额外的开发量。这样,又有一个问题抛出来了,还有没有可以避免修改客户端的实现方法呢?

四、抽象工厂模式(Abstract Factory)

以一个简单的数据库访问程序来系统学习抽象工厂模式。

首先,我们结合上面刚学到的工厂方法模式来实现这个数据库访问程序,类图如下:

这里写图片描述

客户端代码:

public static void main(String[] args) {        User user = new User();        IFactory factory = new SqlServerFactory();//数据库为sqlserver时        //IFactory factory = new AccessFactory();//数据库为access时        IUser iu = factory.createUser();        iu.insert(user);//插入用户        iu.getUser(1);//得到ID为1的用户        Console.Read();    }

如果要换数据库,我们只需要把new SqlServerFactory()改为new AccessFactory(),此时由于多态的关系,使得声明IUser接口的对象iu事先根本不知道是在访问哪个数据库,却可以在运行时很好的完成工作,这就是所谓的业务逻辑与数据库操作解耦。

如果我们在数据库中增加一个Department(部门表),就涉及到解决这种涉及到多个产品系列的问题,那么,抽象工厂模式就可以粉墨登场了。

(IFactory接口下也有一个CreateDepartment()方法,画图时漏掉了)
这里写图片描述

那么,我们需要在原程序的基础上做如下改动:

1、增加一个Department类;
2、增加一个IDepartment接口,用于客户端访问,解除与具体数据库访问的耦合;
3、增加一个SqlserverDepartment类,用于访问SQL Server的Department;
4、增加一个AccessDepartment类,用于访问Access的Department;
5、IFactory接口是定义一个创建访问Department表对象的抽象的工厂接口,增加CreateDepartment()方法,相应的SqlServerFactory和AccessFactory类都要增加该方法;

客户端代码如下:

public static void main(String[] args) {        User user = new User();        IFactory factory = new SqlServerFactory();//数据库为sqlserver时        //IFactory factory = new AccessFactory();//数据库为access时        IUser iu = factory.createUser();        iu.insert(user);//插入用户        iu.getUser(1);//得到ID为1的用户        IDepartment id = factory.createDepartment();        id.insert(dept);//插入部门        id.getDept(1);//得到ID为1的部门        Console.Read();    }

抽象工厂模式(Abstract Method),提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。

这里写图片描述

抽象工厂模式的优点:是易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。我们的设计不能去防止需求的更改,那么我们的理想便是让改动变的最小,现在如果你要更改数据库访问,我们只需要更改具体工厂就可以做到。第二大好处是它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

抽象工厂模式缺点:虽然抽象工厂模式可以很方便的切换两个数据库访问的代码,但是如果你的需求来自增加功能,比如我们现在要增加项目表Project,就至少需要增加三个类,IProject、SqlserverProject、AccessProject,还需要更改IFactory、SqlserverFactory和AccessFactory才可以完全实现。而且,我们的客户端程序类显然不会只是一个,有很多地方都在使用IUser或者IDepartment,而这样的设计,我们在更改数据库的时候,所有的地方都要发生改动,这么大批量的改动显然是非常糟糕的。

使用简单工厂模式来改进抽象工厂模式

这里写图片描述

1 0