工厂方法模式
工厂方法模式(Factory Method Pattern):定义一个用于建立对象的接口,让子类决定将哪个类实例化。工厂方法模式让一个类的实例化延迟到其子类。java
工厂方法模式又简称为工厂模式(Factory Pattern),又可称做虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。python
工厂方法模式是一种类建立型模式。数据库
角色
在工厂方法模式结构图中包含以下几个角色:设计模式
Product(抽象产品):它是定义产品的接口,是工厂方法模式所建立对象的超类型,也就是产品对象的公共父类服务器
ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂建立,具体工厂和具体产品之间一一对应。微信
Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,全部建立对象的工厂类都必须实现该接口。网络
ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。数据结构
与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂能够是接口,也能够是抽象类或者具体类app
示例
抽象产品类 Video框架
public abstract class Video {
public abstract void produce();
}
具体产品类 JavaVideo 和 PythonVideo,须要继承抽象产品类 Video
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}
抽象工厂类 VideoFactory
public abstract class VideoFactory {
public abstract Video getVideo();
}
具体工厂类 JavaVideoFactory 和 PythonVideoFactory,须要继承抽象工厂类 VideoFactory
public class JavaVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
}
public class PythonVideoFactory extends VideoFactory {
@Override
public 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{
@Override
public void produce() {
System.out.println("录制FE课程视频");
}
}
public class FEVideoFactory extends VideoFactory{
@Override
public 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方法就能得到,IteratorCollection.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 return
int lastRet = -1; // index of last element returned; -1 if no such
int 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
的实现类以下

Logger
的实现类
而简单工厂模式应用在 LoggerContext
的 getLogger
方法中,根据参数返回相应的 Logger
对象
后记
点击[阅读原文]可访问个人我的博客:http://laijianfeng.org
关注【小旋锋】微信公众号,及时接收博文推送
参考:
刘伟:设计模式Java版
慕课网java设计模式精讲 Debug 方式+内存分析
本文分享自微信公众号 - 小旋锋(whirlysBigData)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。