设计模式 | 工厂方法模式及典型应用
工厂方法模式
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。
工厂方法模式是一种类创建型模式。
角色
在工厂方法模式结构图中包含如下几个角色:
Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类
ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类
示例
抽象产品类 Video
public abstract class Video {public abstract void produce();
}
复制代码
具体产品类 JavaVideo 和 PythonVideo,需要继承抽象产品类 Video
public class JavaVideo extends Video {@Overridepublic void produce() {System.out.println("录制Java课程视频");}
}public class PythonVideo extends Video {@Overridepublic void produce() {System.out.println("录制Python课程视频");}
}
复制代码
抽象工厂类 VideoFactory
public abstract class VideoFactory {public abstract Video getVideo();
}
复制代码
具体工厂类 JavaVideoFactory 和 PythonVideoFactory,需要继承抽象工厂类 VideoFactory
public class JavaVideoFactory extends VideoFactory {@Overridepublic Video getVideo() {return new JavaVideo();}
}public class PythonVideoFactory extends VideoFactory {@Overridepublic Video getVideo() {return new PythonVideo();}
}
复制代码
客户端类,需要什么产品则通过该产品对应的工厂类来获取,不需要知道具体的创建过程
public class Test {public static void main(String[] args) {VideoFactory pythonVideoFactory = new PythonVideoFactory();VideoFactory javaVideoFactory = new JavaVideoFactory();Video pythonVideo = pythonVideoFactory.getVideo();pythonVideo.produce();Video javaVideo = javaVideoFactory.getVideo();javaVideo.produce();}
}
复制代码
输出
录制Python课程视频
录制Java课程视频
复制代码
当需要增加一个产品 FEVideo 时,只需要增加 FEVideo 具体产品类和 FEVideoFactory 具体工厂类即可,不需要修改原有的产品类和工厂类
public class FEVideo extends Video{@Overridepublic void produce() {System.out.println("录制FE课程视频");}
}public class FEVideoFactory extends VideoFactory{@Overridepublic Video getVideo() {return new FEVideo();}
}
复制代码
修改客户端代码
public class Test {public static void main(String[] args) {VideoFactory feVideoFactory = new FEVideoFactory();Video feVideo = feVideoFactory.getVideo();feVideo.produce();}
}
复制代码
还可以通过反射机制和配置文件配合,连客户端代码都不需要修改
public class Test {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {// 从文件或数据库等外部渠道获取 工厂类名String factoryName = "com.designpattern.factorymethod.JavaVideoFactory";// 通过反射机制获取工厂类Class c = Class.forName(factoryName);VideoFactory factory = (VideoFactory)c.newInstance();// 生产产品Video video = factory.getVideo();video.produce();}
}
复制代码
最终的类图如下所示
工厂方法模式总结
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。
工厂方法模式的主要优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合"开闭原则"。
工厂方法模式的主要缺点
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
适用场景
- 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
- 抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
工厂方法模式的典型应用及源码分析
Java集合接口 Collection 中的工厂方法模式
Collection 中的 iterator 方法如下:
public interface Collection<E> extends Iterable<E> {Iterator<E> iterator();// ...省略
}
复制代码
关于 iterator 方法的介绍:
Java的迭代器只在Collection中有,而Map没有迭代器,它有不同的迭代方法;
迭代器的终极目标:就是用统一的方法来迭代不同类型的集合!可能由于不同集合的内部数据结构不尽相同,如果要自己纯手工迭代的话相互之间会有很大的差别,而迭代器的作用就是统一的方法对不同的集合进行迭代,而在迭代器底层隐藏不同集合之间的差异,从而为迭代提供最大的方便
使用用迭代器迭代的步骤: i. 第一步肯定是先获取集合的迭代器:调用集合的iterator方法就能获得,Iterator Collection.iterator(); ii. 使用迭代器的hasNext、next往下迭代
Iterator的常用方法:boolean hasNext():是否还有下一个元素; Object next():取出下一个元素并返回; void remove(); :从容器中删除当前元素,直接会改变容器中的数据
查看该接口的实现类,可以看到是非常的多
我们仅看其中一个实现类 java.util.ArrayList
,看其对 iterator
方法的实现
public Iterator<E> iterator() {return new Itr();
}/*** An optimized version of AbstractList.Itr*/
private class Itr implements Iterator<E> {int cursor; // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;Itr() {}public boolean hasNext() {return cursor != size;}@SuppressWarnings("unchecked")public E next() {// ...省略...}public void remove() {// ...省略...}@Override@SuppressWarnings("unchecked")public void forEachRemaining(Consumer<? super E> consumer) {// ...省略...}final void checkForComodification() {// ...省略...}
}
复制代码
Itr
类实现了 iterator
接口,iterator
接口正是 Collection
接口中 iterator
方法的返回类型,其代码如下:
public interface Iterator<E> {boolean hasNext();E next();default void remove() {throw new UnsupportedOperationException("remove");}default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());}
}
复制代码
由此可见,Collection
接口扮演了抽象工厂角色,工厂方法为 iterator()
,Collection
的实现类譬如 ArrayList
扮演了具体工厂角色,而抽象产品为 Iterator
接口,具体产品为 Itr
类
java.net 网络包中的工厂方法模式
URLStreamHandlerFactory 接口为 URL 流协议处理程序定义一个工厂。URL 类使用它可为特定的协议创建 URLStreamHandler
public interface URLStreamHandlerFactory {/*** Creates a new {@code URLStreamHandler} instance with the specified protocol.** @param protocol the protocol ("{@code ftp}", "{@code http}", "{@code nntp}", etc.).* @return a {@code URLStreamHandler} for the specific protocol.* @see java.net.URLStreamHandler*/URLStreamHandler createURLStreamHandler(String protocol);
}
复制代码
该接口的实现类为 sun.misc.Launcher
中的内部类 Factory
private static class Factory implements URLStreamHandlerFactory {private static String PREFIX = "sun.net.www.protocol";private Factory() {}public URLStreamHandler createURLStreamHandler(String var1) {String var2 = PREFIX + "." + var1 + ".Handler";try {Class var3 = Class.forName(var2);return (URLStreamHandler)var3.newInstance();} catch (ReflectiveOperationException var4) {throw new InternalError("could not load " + var1 + "system protocol handler", var4);}}
}
复制代码
可以看到 createURLStreamHandler
方法的实现为:传入参数,拼接前缀和后缀,之后通过反射机制获取创建一个 URLStreamHandler
对象
URLStreamHandler
是一个抽象类,其中的方法如下图,只有 openConnection
为抽象方法,其他方法均有具体实现
关于URLStreamHandler:
抽象类URLStreamHandler是所有流协议处理程序的通用超类。 流协议处理程序知道如何为特定协议类型建立连接,例如http或https
其子类有如下(19个):
查看其中一个子类譬如 sun.net.www.protocol.http.Handler
public class Handler extends URLStreamHandler {protected String proxy;protected int proxyPort;protected int getDefaultPort() {return 80;}public Handler() {this.proxy = null;this.proxyPort = -1;}public Handler(String var1, int var2) {this.proxy = var1;this.proxyPort = var2;}protected URLConnection openConnection(URL var1) throws IOException {return this.openConnection(var1, (Proxy)null);}protected URLConnection openConnection(URL var1, Proxy var2) throws IOException {return new HttpURLConnection(var1, var2, this);}
}
复制代码
该类实现的 openConnection
方法的返回值类型为 URLConnection
,最终返回了一个 HttpURLConnection
对象
我们又继续看 java.net.URLConnection
,这也是一个抽象类
URLConnection介绍:
- URLConnection是一个功能强大的抽象类,它表示指向URL指定资源的活动连接。 与URL类相比,它与服务器的交互提供了更多的控制机制。尤其服务器是HTTP服务器,可以使用URLConnection对HTTP首部的访问,可以配置发送给服务器的请求参数。当然也可以通过它读取服务器的数据以及向服务器写入数据.
- URLConnection是Java的协议处理器机制的一部分。协议处理器机制是将处理协议的细节与特定数据类型分开。如果要实现一个特定的协议,则实现URLConnection的子类即可。程序运行时可以将该子类作为一个具体的协议处理器来使用。
- 使用URLConnection类的步骤:1. 构造一个URL对象;2. 调用该URL的openConnection()获取一个URLConnection;3. 配置这个URLConnection;4. 读取首部字段;5. 获得输入流并读取数据;6. 获得输出流并写入数据;7. 关闭连接
其子类有23个
我们可以画出他们的关系图如下所示
由此可知:抽象工厂角色为 URLStreamHandlerFactory
,工厂方法为 createURLStreamHandler
,抽象产品角色为 URLStreamHandler
,具体产品角色为 URLStreamHandler
的子类譬如 sun.net.www.protocol.http.Handler
、sun.net.www.protocol.ftp.Handler
等
同时,URLStreamHandler
也扮演了抽象工厂角色,工厂方法为 openConnection
,URLStreamHandler
的子类譬如 sun.net.www.protocol.http.Handler
也扮演了具体工厂角色,抽象产品为 URLConnection
,具体产品角色为 URLConnection
的子类如 sun.net.www.protocol.http.HttpURLConnection
等
Logback 中的工厂方法模式
在上一篇文章《设计模式 | 简单工厂模式及典型应用》 介绍的 Logback 里有简单工厂模式,其实也有工厂方法模式,画图如下
可以看出,抽象工厂角色为 ILoggerFactory
接口,工厂方法为 getLogger
,具体工厂角色为 LoggerContext
、NOPLoggerFactory
、SubstituteLoggerFactory
等,抽象产品角色为 Logger
,具体产品角色为 Logger
的实现类如下
而简单工厂模式应用在 LoggerContext
的 getLogger
方法中,根据参数返回相应的 Logger
对象
参考:
刘伟:设计模式Java版
慕课网java设计模式精讲 Debug 方式+内存分析
更多内容请访问我的个人博客:laijianfeng.org/
打开微信扫一扫,关注【小旋锋】微信公众号,及时接收博文推送
设计模式 | 工厂方法模式及典型应用相关推荐
- 4. 星际争霸之php设计模式--工厂方法模式
题记 ============================================================================== 本php设计模式专辑来源于博客(jy ...
- java 工厂方法_java设计模式-工厂方法模式
1.工厂方法(FactoryMethod)模式的定义 定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中.这满足创建型模式中所要求的"创建与使用相分离" ...
- python工厂模式 django_python设计模式-工厂方法模式
题目:假设你有一个 pizza 店,功能包括下订单.做 pizza,你的代码会如何写呢? def order_pizza(): pizza = Pizza() pizza.prepare() pizz ...
- 工厂方法模式_1天1个设计模式——工厂方法模式
意图 工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型. 问题 假设你正在开发一款物流管理应用.1.0版本只能支持处理卡车运输,因此大部分的代码都位 ...
- [设计模式] ------ 工厂方法模式
工厂方法模式 手机厂造手机,肥皂厂造肥皂,药品厂造药品. 假设有一大类对象A1,A2,A3,A4-他们都有一个共同的父类A A1可以想成是手机,A2可以想成肥皂- (下面例子的A1Factory就是手 ...
- JAVA设计模式 - 工厂方法模式
工厂方法模式(Factory Pattern) 是一种创建型设计模式 , 它是Java中最常用的设计模式之一 . 1 . 工厂方法模式的定义 定义一个创建对象的接口 , 让子类决定实例化哪一个类 . ...
- Java设计模式—工厂方法模式抽象工厂模式
工厂方法模式与抽象工厂模式都是设计模式中重要而且常见的模式. 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 通用类图如下: 在 ...
- C#设计模式--工厂方法模式
0.C#设计模式-简单工厂模式 设计模式: 工厂方法模式(Factory Method Pattern) 介绍:简单工厂模式是要在工厂类中通过数据来做个决策,在工厂类中的多个类中实例化出来其中一个要用 ...
- 设计模式 - 工厂方法模式 - 基于 Java 实现
常见和工厂有关的设计模式 和工厂相关的设计模式主要有三种,工厂方法模式,抽象工厂模式: 在前面的博客中介绍了简单工厂模式,本篇博客主要介绍的是工厂方法模式 什么是工厂方法模式 工厂方法模式是简单工厂的 ...
最新文章
- 导致命令注入漏洞的php函数,PHP安全-函数
- 在Linux上限制远程登陆的IP
- 工作中常用的第三放的框架
- 敏捷开发回顾:使团队更强大pdf
- 修复IE9-- safari 的sort方法
- Juventas, the Roman Goddess of Youth [ Juventas, 罗马青春女神]
- 组态王能直接读取仪表数据吗_液晶多功能网络电力仪表PD800H
- git常用命令+git规范(附merge合并及冲突解决)
- cacti监控java,Cacti监控tomcat的方法
- 涨价妥妥的!一加7 Pro欧洲价格曝光:顶配或超6000
- 计算机仿真技术物流,基于计算机仿真技术的物流实验室建设
- 设计模式-解释器模式
- 第三阶段:数据存储与计算(离线场景):3.2 数据存储hdfs
- 网易2018校园招聘题目
- Day15 --框架集合 Collection集合 和 List 集合
- 牛客寒假算法基础集训营_I炫酷镜子(模拟)
- 重新定义一个全新的区块链运行架构:他们的技术有何不同?
- CIH噩梦重现 二手主板市场暗藏“谍影”BIOS病毒
- 基于ssm(spring mybatis) java超市进销存系统源码设计
- HDS VSP/VM 高端存储更换MPB-2MC