热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:Java设计模式百例工厂方法模式

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java设计模式百例-工厂方法模式相关的知识,希望对你有一定的参考价值。本文源码见

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java设计模式百例 - 工厂方法模式相关的知识,希望对你有一定的参考价值。



本文源码见:https://github.com/get-set/get-designpatterns/tree/master/factory-method

工厂方法模式同简单工厂模式一样,也是创建类模式,又叫做虚拟构造(Virtual Constructor)模式或多态工厂(Polymorphic Factory)模式。其用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。

上篇说到,简单工厂模式并未做到完全的“开闭原则”。回顾一下,“开”即对扩展开放,这点是没错的,简单工厂模式的初衷之一就是方便增加“产品类型”的时候;“闭”即对修改关闭,这点其实并未做到,当需要增删改“产品类型”的时候,工厂类必须要修改,因为工厂类“知道”如何创建所有的产品类型的对象。

那么工厂方法模式就是为了完全满足“开闭原则”,即在简单工厂模式的基础上,做到当增加产品类型的时候,无需改动现有的代码。继续用上篇的例子:

例子

仍然是做一个画图软件,可以画矩形、三角形和圆形等,每一种图形都用一个类来管理: 
Rectangle 
Circle 
Triangle 
每个类都有各自的draw()方法,共同实现Shape接口。

Shape.java

public interface Shape {
   void draw();
}

Rectangle.java

public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Draw a rectangle.");
   }
}

Triangle.java

public class Triangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Draw a triangle.");
   }
}

Circle.java

public class Circle implements Shape {
   @Override
   public void draw() {
      System.out.println("Draw a circle.");
   }
}

以上几个类都没有变化,有变化的是工厂类,工厂类也采用基于接口的设计,由不同的具体工厂类负责相应对象的创建:

ShapeFactory.java

public interface ShapeFactory {
    Shape getShape();
}

RectangleFactory.java

public class RectangleFactory implements ShapeFactory {
    public Shape getShape() {
        return new Rectangle();
    }
}

CircleFactory.java

public class CircleFactory implements ShapeFactory{
    public Shape getShape() {
        return new Circle();
    }
}

Triangle.Factory.java

public class CircleFactory implements ShapeFactory{
    public Shape getShape() {
        return new Circle();
    }
}

那么在需要某个形状的时候,就通过相应的具体工厂类创建即可:

Client.java

public class Client {
    public static void main(String[] args) {
        ShapeFactory factory = new CircleFactory();
        Shape c = factory.getShape();
        c.draw();
    }
}

再来看一下类图:

技术分享

与简单工厂模式对比以下:

技术分享

区别就在于简单工厂模式下的一个具体的工厂类转换成了基于接口ShapeFactory的三个具体工厂类。

好处也是明显的:当增加一个新的形状类型的时候,不需要对现有代码做任何更改,增加一个相应的实现了ShapeFactory的具体工厂类即可。可见,工厂方法模式能够完全做到“开闭原则”。

这个例子仍然是不恰当的,因为无论如何这一套模式设计比原始的实现方式有更加复杂了。所以再次赘述一遍注意事项,以上例子是为了说明工厂方法模式,但并不是一个合理的应用。复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

Java中的应用

下边看一下合理的应用场景是如何的,在Java中的实际应用的例子(来自《Java与模式》):

* 1. Java聚集中的应用 *

Java聚集是一套设计精良的数据结构实现,主要的Java聚集都实现自java.util.Collection接口,这个接口的父接口Iterable规定所有的Java聚集都必须提供一个iterator()方法,返还一个Iterator类型的对象:

java.lang.Iterable.java

public interface Iterable {
    ... ...
    Iterator iterator();
    ... ...
}

ArrayLis是我们常用的一个Collection实现类,其iterator()方法实现如下:

java.util.ArrayList.java

public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable {
    ... ...
    // 实现iterato接口,返回一个Iterator对象
    public Iterator iterator() {
        return new Itr();
    }
    private class Itr implements Iterator {
    ... ...
    }
    ... ...
}

可见,ArrayList类的iterator()方法就是一个具体工厂类的工厂方法,而Collection就是一个抽象工厂。除了ArrayList还有LinkedList等等具体实现类。

总结

从上边的例子可以看到,工厂方法模式其实是将“面向接口”编程的思路应用在了工厂类上,这样有几个方便的地方:

  1. 做到了完全的“开闭原则”,因为增加新的“产品”和相应的“工厂”均不需修改现有代码;

  2. 工厂设计模式通常应用在复杂的对象创建场景中,因此面临多层的继承关系,比如ArrayList实现了List接口,而后者继承自Collect接口,Collect又继承自Iterator接口。有时候具体工厂方法与具体产品是有层次对应关系的,比如:

技术分享

这种情况也是只有一个工厂类的简单工厂模式所无法满足的。



推荐阅读
  • [转] JavaScript中in操作符(for..in)、Object.keys()和Object.getOwnPropertyNames()的区别
    ECMAScript将对象的属性分为两种:数据属性和访问器属性。每一种属性内部都有一些特性,这里我们只关注对象属性的[[Enumerable]]特征,它表示是否通过for-in循环 ... [详细]
  • 本文通过SystemTap工具详细分析了lvextend命令在SUSE12sp3系统上的执行流程。首先介绍了必要的软件安装步骤,随后展示了如何编写并运行SystemTap脚本来追踪命令执行过程中的函数调用,最后结合实际输出结果对关键函数进行了深入分析。 ... [详细]
  • 本文深入探讨了Java注解的基本概念及其在现代Java开发中的应用。文章不仅介绍了如何创建和使用自定义注解,还详细讲解了如何利用反射机制解析注解,以及Java内建注解的使用场景。 ... [详细]
  • TensorFlow核心函数解析与应用
    本文详细介绍了TensorFlow中几个常用的基础函数及其应用场景,包括常量创建、张量扩展以及二维卷积操作等,旨在帮助开发者更好地理解和使用这些功能。 ... [详细]
  • 设计模式笔记12:迭代器模式(Iterator Pattern) ... [详细]
  • Android实战:使用ProgressBar与AsyncTask实现数据异步加载
    本文介绍如何利用ProgressBar和AsyncTask在Android应用中实现数据的异步加载。包括加载数据的不同状态下的UI展示,如加载中、加载成功及加载失败时的界面处理。 ... [详细]
  • 本文详细探讨了UML用例图中的两种重要关系——包含关系和扩展关系,通过具体示例解析这两种关系的应用场景及其实现方式。 ... [详细]
  • 在上一期文章中,我们探讨了FastDev4Android项目中PullToRefreshListView组件的使用方法。本期将继续探讨该框架中的另一个重要组件——ACache数据缓存器,详细介绍其工作原理及如何在项目中有效利用。 ... [详细]
  • 本文详细介绍了C++标准模板库(STL)中各容器的功能特性,并深入探讨了不同容器操作函数的异常安全性。 ... [详细]
  • chrome安装reactdevtools开发工具
    我开始安装react-devtools的时候百度了一波,都是写的不清不楚,官网又都是英文的也不是完全理解,经过一番折腾出来以后,写个文档记录一下,也可避免新手首次安装走弯路我安装react-devtools的前提是本地安装了git以及node我相信准备学react的同学,应该都有了解使用1.首先打开官网:https:github.comfacebook ... [详细]
  • 本文详细介绍了RPM包构建过程中Spec文件的结构和各部分的作用,包括包描述、准备阶段、构建过程、安装步骤、清理操作以及文件列表等关键环节。同时,提供了关于RPM宏命令、打包目录结构及常见标签的深入解析。 ... [详细]
  • 每位开发者都应该拥有一个展示自我技能与分享知识的空间——个人技术博客。本文将指导你如何使用静态网站生成器Hexo结合GitHub Pages搭建这样一个平台。 ... [详细]
  • 本文基于https://major.io/2014/05/13/coreos-vs-project-atomic-a-review/的内容,对CoreOS和Atomic两个操作系统进行了详细的对比,涵盖部署、管理和安全性等多个方面。 ... [详细]
  • InnoSetup打包的软件需要在安装和卸载时检查程序是否在运行,不然会安装失败或者卸载不完全,网上搜了一下,有几种方法: ... [详细]
  • DP:InitiallyIthinkof1DDP,dp[i]standsfortheshorteststringoffirsticharacters,then:dp[i]minLe ... [详细]
author-avatar
晨曦--雨荷
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有