【JAVA设计模式】工厂方法模式(Factory Method)无标题文章

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method 使一个类的实例化延迟到其子类

正如名字所示,工厂方法模式与简单工厂有几分相似。关于差异,后文将会介绍。

案例

例如有这样一个应用:实现一个导出数据的应用框架,来让客户选择导出方式,比如导出成文本格式,数据格式,xml格式等等。
依然,为了面向接口编程。我们首先定义一个接口,抽象了导出数据功能。

/**
 * 各种导出形式的操作都遵循此接口
 * Created by jerry on 16-4-21.
 */
public interface ExportFileApi {
    /**
     * 导出内容
     * @param data 导出数据
     * @return 是否成功
     */
    public boolean export(String data);
}

现在如果我想要实现把数据导出文本格式或者导出成其他格式,那么实现此接口即可。

/**
 * 将数据导出到txt文件中去
 * Created by jerry on 16-4-21.
 */
public class ExportTxtFile implements ExportFileApi{

    @Override
    public boolean export(String data) {
        System.out.println("导出数据: "+ data + " 到txt中");
        return true;
    }
}
/**
 * 导出数据到数据库中
 * Created by jerry on 16-4-21.
 */
public class ExportDB implements ExportFileApi{

    @Override
    public boolean export(String data) {
        System.out.println("导出数据: " +data+ " 到数据库中");
        return true;
    }
}

问题

试想一个问题,当我们在设计时,我们还没有想好将来究竟是用哪个具体实现,例如究竟是导出到文件呢?还是数据库呢?还是其他?换句话说,关于具体采用哪种,我想之后再做决定。这时,为了让框架层代码能够不受影响,就需要工厂方法模式。

使用模式的情况

工厂方法模式的主要功能就是让父类在不知道具体实现的情况下,完成自身的功能调用,而具体实现延迟到子类来实现。通常情况下,父类是一个抽象类,里面包含了一个抽象方法,此方法即工厂方法。

/**
 * 实现导出数据操作功能
 * Created by jerry on 16-4-21.
 */
public abstract class ExportOperate {

    /**
     * 导出数据
     * @param data
     * @return
     */
    public boolean export(String data) {
        ExportFileApi api = factoryMethod();
        return api.export(data);
    }

    /**
     * 工厂方法,创建导出的文件对象的接口对象
     * @return
     */
    protected abstract ExportFileApi factoryMethod();
}

这样以后,继承ExportOperate的类必须复写factoryMethod()方法,即由子类来完成具体实现,在父类中无须关注到底是哪个具体实例负责具体的实现,父类只须面向接口编程,大大减少了耦合度。

/**
 * 在这里真正创建具体对象
 * Created by jerry on 16-4-21.
 */
public class ExportTxtFileOperate extends ExportOperate {

    @Override
    protected ExportFileApi factoryMethod() {
        return new ExportTxtFile();
    }
}
/**
 * Created by jerry on 16-4-21.
 */
public class ExportDBOperate extends ExportOperate {

    @Override
    protected ExportFileApi factoryMethod() {
        return new ExportDB();
    }
}

客户端调用如下:

/**
 * 测试类
 * Created by jerry on 16-4-21.
 */
public class Client {

    public static void main(String[] args) {
        ExportOperate operate = new ExportDBOperate();
        operate.export("test");
    }
}

参数化工厂方法

工厂方法的设计中,可以使用参数,这样就可以通过参数决定到底使用哪一个具体的实现。
如果采用参数化工厂方法,那么通常工厂方法不再抽象,而是变为普通方法,并且提供默认的实现。此时,既然有了默认实现,工厂方法所在类也无须变为抽象,普通类即可。这样客户端可以直接使用这个类。

/**
 * 实现导出数据操作功能
 * Created by jerry on 16-4-21.
 */
public class ExportOperateByPara {

    /**
     * 导出数据
     * @param data
     * @param type 类型选择
     * @return
     */
    public boolean export(int type, String data) {
        ExportFileApi api = factoryMethod(type);
        return api.export(data);
    }

    /**
     * 工厂方法,根据参数创建导出的文件对象的接口对象
     * @param type
     * @return
     */
    protected ExportFileApi factoryMethod(int type) {
        ExportFileApi api = null;

        if (type == 1) {
            api = new ExportTxtFile();
        } else if (type == 2) {
            api = new ExportDB();
        }
        return api;
    }

}

由于有了默认实现,直接使用即可,客户端调用如下:

/**
 * 测试类2
 * Created by jerry on 16-4-21.
 */
public class Client2 {

    public static void main(String[] args) {
        ExportOperateByPara operate = new ExportOperateByPara();
        operate.export(2,"test");
    }
}

之所以使用参数化工厂方法,是因为其扩展起来会非常方便,已有的代码不会变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。
例如,我们要扩展一个导出xml文件的类:

/**
 * 导出到xml
 * Created by jerry on 16-4-21.
 */
public class ExportXml implements ExportFileApi {

    @Override
    public boolean export(String data) {
        System.out.println("导出数据: " + data + " 到xml文件");
        return true;
    }
}

加入新的实现类,这里可以让子类选择性覆盖,进行功能增强,或者不想覆盖还可以返回去让父类来实现。

/**
 * Created by jerry on 16-4-21.
 */
public class ExportXmlOperate extends ExportOperateByPara {

    /**
     * 覆写工厂方法
     * @param type
     * @return
     */
    @Override
    protected ExportFileApi factoryMethod(int type) {
        ExportFileApi api = null;
        // 这里是一个强大功能,可以让子类选择性覆盖,不想覆盖的方法可以返回去让父类实现
        if (type == 3) {
            api = new ExportXml();
        } else {
            api = super.factoryMethod(type);
        }
        return api;
    }
}
/**
 * 测试类2
 * Created by jerry on 16-4-21.
 */
public class Client2 {

    public static void main(String[] args) {
        ExportOperateByPara operate = new ExportXmlOperate();
        operate.export(1,"test1");
        operate.export(2,"test2");
        operate.export(3,"test3");
    }
}

程序运行如下:

导出数据: test1 到txt中
导出数据: test2 到数据库中
导出数据: test3 到xml文件

这个易扩展性的获得,是因为工厂方法给子类提供了一个钩子,使得子类可以操作父类的方法,使得扩展新的对象版本变得非常容易。

与简单工厂模式的区别

如果把ExportOperateByPara中的export方法删掉,那么发现此类就是一个简单工厂类。所以两者其实非常相似,在具体实现上都是“选择实现”。但是不同点在于:简单工厂直接在工厂类里面进行“选择实现”,而工厂方法会把这个工作延迟到子类来实现。

总结

本质:延迟到子类来选择实现。

何时使用工厂方法模式:

  1. 如果一个类需要创建某个接口的对象,但是有不知道具体的实现。此时可选用工厂方法模式,把创建对象的工作延迟到子类中实现。
  2. 如果一个类本身就希望由它的子类来创建所需的对象的时候,应该使用工厂方法模式。

我的博客:http://shenchao.me/
Github: https://github.com/jerry-sc/designPattern
Reference:《研磨设计模式》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 导出数据的应用框架## 考虑这样一个实际应用:实现一个导出数据的应用框架,来让客户选择数据...
    七寸知架构阅读 6,892评论 6 75
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 4,003评论 1 15
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,839评论 18 399
  • 简单工厂模式虽然简单,但存在一个很严重的问题。当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建...
    justCode_阅读 1,231评论 1 9
  • 每日我都以愉快的心情思念你 在这宇宙一块安静的地方 等候你的佳音 你是我无处不在的天使 主宰着我的生与死 你的喜怒...
    烧火一条柴阅读 353评论 0 2