1. 简单工厂模式

在介绍工厂方法模式之前,先介绍一下简单工厂模式。虽然简单工厂模式不属于GoF 23种设计模式,但通常将它作为学习其他工厂模式的入门,并且在实际开发中使用的也较为频繁。

(1) 定义

简单工厂模式(Simple Factory Pattern):定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例一般具有共同的父类。因为在简单工厂模式中,用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method),它属于类创建型模式。

(2) 问题

在生活中,当我们需要某类产品时,我们的做法是去生产它的工厂(或销售它的商店)去购买它。比如我们需要一只笔来写字,我们需要到文具店去购买。在购买时,我们只需要告诉服务员我们需要一只钢笔或者毛笔,然后我们便可以付钱得到。至于笔是如何生产的,我们根本不关心(当然质量还是要考虑的)。同样在编程时,我们是否能事先设计出一些工厂类,当我们需求某个类时,我们找到相应的工厂类,调用它的生成产品方法,同时传进我们需要产品的名称,该方法就可以返回出我们需要的对象。

例如,我们需要设计一套图表库供人调用。目前该图表库只包含折线图,饼状图,柱状图,以后再对其进行扩增。设计需要达到的效果是调用者只需要调用某个类的某一方法,并给出所需的图标的类型名称,该方法会自动创建该图表类,并返回给调用者。

(3) 解决方案

我们首先对所有的图表类抽象出一个公共的接口或抽象类,然后所有的具体的图表类需要实现该接口,最终只需要面向这个接口编程即可。

① 抽象产品类

public abstract class Char {

    protected float[] data;

    public Char(float[] data) {
this.data = data;
System.out.println("初始化一些公共的操作...");
} public void setData(float[] data) {
this.data = data;
} public abstract void display();
}

② 实现具体产品类

public class LineChar extends Char {

    public LineChar() {
this(null);
} public LineChar(float[] data) {
super(data);
System.out.println("初始化LineChar");
} @Override
public void display() {
System.err.println("将Data以LineChar形式显示");
} }

其他的具体类也以同样的形式创建,这里省略。

③ 构建工厂类

public class CharFactory {

    public static final int LINE_CHAR = 0;
public static final int HISTOGRAM_CHAR = 1;
public static final int PIE_CHAR = 2; public static Char getChar(int type) {
switch (type) {
case HISTOGRAM_CHAR:
return new HistogramChar();
case PIE_CHAR:
return new PieChar();
case LINE_CHAR:
default:
return new LineChar();
}
}
}

④ 测试

public static void main(String[] args) {
Char c = CharFactory.getChar(CharFactory.LINE_CHAR);
c.setData(new float[] { 1, 2, 3, 4 });
c.display();
}

打印结果:

初始化一些公共的操作...
初始化LineChar
将Data以LineChar形式显示

2. 工厂方法模式

(1) 定义

工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又称为工厂模式(Factory Pattern)或虚拟构造器模式(Virtual Constructor Pattern)又或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。

(2) 问题

简单工厂模式虽然简单,但是存在一个严重的问题:当我们引入新的产品时,需要对工厂类的源码进行修改。例如上例中,如果我们又实现了一些图表类,需要将他们纳入工厂的生产范围,我们需要再增加几个case代码块来进行判断。一来,随着产品类的增加,工厂类的代码也会等比例增加,显得十分累赘;二来这种需要修改原先设计的做法违背了开闭原则。

有没有一种方案能够解决这个问题呢?

以日志器为例子。我们在开发或生产环境下经常需要打印一些日志,因此需要一个日志器来打印日志。根据日志输出的位置的不同,日志器又有很多种。目前实现了输出日志到文件和输出日志到数据库这两种日志器,以后再做扩展,现同样需要设计一个工厂类,但要解决掉简单工厂模式的缺陷问题。

(3) 解决方案

①  抽象产品类和工厂类

public interface Logger {
void log();
}
public interface LoggerFactory {
Logger createLogger();
}

② 实现产品类和工厂类

public class DatabaseLogger implements Logger {

    @Override
public void log() {
System.out.println("正在打印数据库日志...");
} }
public class DatabaseLoggerFactory implements LoggerFactory {

    @Override
public Logger createLogger() {
return new DatabaseLogger();
}
}

这里均只给出Database的实现。

③ 测试

@Test
public void test1() {
LoggerFactory factory = new DatabaseLoggerFactory();
Logger logger = factory.createLogger();
logger.log();
}

输出结果:

正在打印数据库日志...

④ 改进

我们可以引入配置文件来“动态”改变LoggerFactory的实例。

编写一个读取xml配置文件的工具类:

public class XMLUtil {

    public static Object getBeanFromXml(String beanName) {
if (beanName == null || beanName.isEmpty()) {
return null;
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("config.xml"));
NodeList beansList = document.getElementsByTagName("beans");
if (beansList.getLength() == 0) {
return null;
}
NodeList beanList = beansList.item(0).getChildNodes();
for (int i = 0; i < beanList.getLength(); i++) {
if (beanList.item(i).getNodeType() == Node.ELEMENT_NODE) {
if (beanName.equals(beanList.item(i).getNodeName())) {
return Class.forName(beanList.item(i).getTextContent()).newInstance();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

配置文件config.xml这样写

<?xml version="1.0" encoding="UTF-8"?>

<beans>
<loggerFactory>factory.factory.FileLoggerFactory</loggerFactory>
</beans>

这样调用:

@Test
public void test2() {
LoggerFactory factory = (LoggerFactory) XMLUtil.getBeanFromXml("loggerFactory");
if (factory != null) {
factory.createLogger().log();
}
}

输出结果同上。如果我们需要切换日志工厂,只需修改配置文件中loggerFactory结点的值即可。

工厂方法模式——创建型模式02的更多相关文章

  1. FactoryMethod工厂方法模式(创建型模式)

    1.工厂方法模式解决的问题 现在有一个抽象的游戏设施建造系统,负责构建一个现代风格和古典风格的房屋和道路. 前提:抽象变化较慢,实现变化较快(不稳定) 整个抽象的游戏设施建造系统相对变化较慢,本例中只 ...

  2. Prototype原型模式(创建型模式)

    1.原型模式解决的问题 现在有一个抽象的游戏设施建造系统,负责构建一个现代风格和古典风格的房屋和道路. 前提:抽象变化较慢,实现变化较快(不稳定) 整个抽象的游戏设施建造系统相对变化较慢,本例中只有一 ...

  3. 设计模式(三): FACTORY工厂模式 -- 创建型模式

    1.定义 定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类. 2.适用场景 1.第一种情况是对于某个产品,调用者清楚地知道应该使用哪个具体 ...

  4. 设计模式(五):PROTOTYPE原型模式 -- 创建型模式

    1.定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 2.适用场景 原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对 ...

  5. 设计模式(二): BUILDER生成器模式 -- 创建型模式

    1.定义 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式. 2.适用场景 1. 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式 ...

  6. 设计模式(四):SIMPLE FACTORY简单工厂模式 -- 创建型模式

    1.定义 简单工厂模式又称静态工厂方法模式.重命名上就可以看出这个模式一定很简单.它存在的目的很简单:定义一个用于创建对象的接口. 2.适用场景 如果一个客户要一款宝马车,一般的做法是客户去创建一款宝 ...

  7. C#设计模式--工厂模式(创建型模式)

    一.简单工厂模式(UML类图): 核心类代码: public class Calc { public double NumberA { get; set; } public double Number ...

  8. 工厂模式/factory模式/创建型模式

    工厂模式 普通工厂模式 原本需要new出来的对象,通过一个类的方法去搞定,Factory.build(parameter),类似这种. public interface Sender { public ...

  9. 设计模式(一): abstract factory抽象工厂模式 -- 创建型模式

    1.定义 为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类. 2.适用场景 1.一个系统要独立于它的产品创建.组合和表示. 2.一个系统要由多个产品系列中的一个来配置. 3.当你要 ...

随机推荐

  1. ASP.NET Aries 入门开发教程3:开发一个列表页面及操控查询区

    前言: Aries框架毕竟是开发框架,所以重点还是要写代码的,这样开发人员才不会失业,哈. 步骤1:新建html 建一个Html,主要有三步: 1:引入Aries.Loader.js 2:弄一个tab ...

  2. Jexus 5.8.2 正式发布为Asp.Net Core进入生产环境提供平台支持

    Jexus 是一款运行于 Linux 平台,以支持  ASP.NET.PHP 为特色的集高安全性和高性能为一体的 WEB 服务器和反向代理服务器.最新版 5.8.2 已经发布,有如下更新: 1,现在大 ...

  3. Centos——安装JDK

    写在前面: Just mark! 创建linux虚拟机的时候经常要安装JDK,配置环境变量,却又经常忘记,这里记录一下. 环境:Centos-6.8-x86_64-minimal JDK :jdk-7 ...

  4. Yii1.1的验证规则

    在Yii1.1的数据验证是由CValidator完成,在CValidator中提供了各种基本的验证规则 <?php public static $builtInValidators=array( ...

  5. CRL快速开发框架系列教程九(导入/导出数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. TortoiseGit 文件比对工具使用 Beyond Compare 和 DiffMerge

    TortoiseGit 内置的文件比对工具是 TortoiseMerge,用于文件比对和解决冲突合并,TortoiseGit 还支持外部的比对工具使用,比如 Beyond Compare 和 Diff ...

  7. JavaScript中事件处理

    先看看下面一道题目,请评价以下代码并给出改进意见: if (window.addEventListener) {//标准浏览器 var addListener = function(el, type, ...

  8. MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决

    一.简介 MySQL是最流行的开放源码SQL数据库管理系统,它是由MySQL AB公司开发.发布并支持的.有以下特点: MySQL是一种数据库管理系统. MySQL是一种关联数据库管理系统. MySQ ...

  9. JavaScript基础知识总结(一)

    当我们接触一种新语言时,首先要先了解它,对它有一定的理论认识. 那么,什么是JavaScript呢? JavaScript是一种脚本语言,由web浏览器进行解释和执行.它包括ECMAScript.DO ...

  10. EF上下文对象线程内唯一性与优化

    在一次请求中,即一个线程内,若是用到EF数据上下文对象,就创建一个,这也加是很多人的代码中习惯在使用上下文对象时,习惯将对象建立在using中,也是为了尽早释放上下文对象, 但是如果有一个业务逻辑调用 ...