工厂方法模式 工厂方法模式的角色

时间:2023-05-02 23:25/span> 作者:tiger 分类: 新知 浏览:5436 评论:0

扫码关注【小旋锋】微信公众号

工厂方法模式

工厂方法模式(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
 
{
 
@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方法就能获得,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 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或http

其子类有如下(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 方式+内存分析

文章评论