工厂方法模式(Factory Method Pattern)理论篇
一、什么是工厂方法模式
工厂方法模式
属于类的创建型模式,它又被称为
多态工厂模式
(
Factory Method
Pattern
)
。
工厂方法模式
的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。
核心工厂类
不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责实现一些通用的方法和定义一些具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
二 、模式结构和说明
工厂方法模式的结构如图3所示:
图1 工厂方法模式结构示意图
Product
:
定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。
ConcreteProduct
:
具体的Product接口的实现对象。
Creator
:
创建器,声明工厂方法,工厂方法通常会返回一个Product类型的实例对象,而且多是抽象方法。也可以在Creator里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Product类型的实例对象。
ConcreteCreator
:
具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例。
三、 工厂方法模式示例代码
(1)
先看看Product的定义,示例代码如下:
/*** 工厂方法所创建的对象的接口*/public interface Product {//可以定义Product的属性和方法}
(2)
再看看具体的Product的实现对象,示例代码如下:
/*** 具体的Product对象*/public class ConcreteProduct implements Product {//实现Product要求的方法}
(3)
接下来看看创建器的定义,示例代码如下:
/*** 创建器,声明工厂方法*/public abstract class Creator {/*** 创建Product的工厂方法* @return Product对象*/protected abstract Product factoryMethod ();/*** 示意方法,实现某些功能的方法*/public void someOperation () {//通常在这些方法实现中,需要调用工厂方法来获取Product对象Product product = factoryMethod();}}
(4)
再看看具体的创建器实现对象,示例代码如下:
/*** 具体的创建器实现对象*/public class ConcreteCreator extends Creator {protected Product factoryMethod () {//重定义工厂方法,返回一个具体的Product对象return new ConcreteProduct();}}
四、 认识工厂方法模式
4.1、工厂方法模式的功能
工厂方法的主要功能是让父类在不知道具体实现的情况下,完成自身的功能调用,而具体的实现延迟到子类来实现。
这样在设计的时候,不用去考虑具体的实现,需要某个对象,把它通过工厂方法返回就好了,在使用这些对象实现功能的时候还是通过接口来操作,这非常类似于IoC/DI的思想,这个在后面给大家稍详细点介绍一下。
4.2、实现成抽象类
工厂方法的实现中,通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法。
这里要注意一个问题,子类在实现这些抽象方法的时候,通常并不是真的由子类来实现具体的功能,而是在子类的方法里面做选择,选择具体的产品实现对象。
父类里面,通常会有使用这些产品对象来实现一定的功能的方法,而且这些方法所实现的功能通常都是公共的功能,不管子类选择了何种具体的产品实现,这些方法的功能总是能正确执行
。
4.3、实现成具体的类
当然也可以把父类实现成为一个具体的类,这种情况下,通常是在父类中提供获取所需对象的默认实现方法,这样就算没有具体的子类,也能够运行。
通常这种情况还是需要具体的子类来决定具体要如何创建父类所需要的对象。也把这种情况称为工厂方法为子类提供了挂钩,通过工厂方法,可以让子类对象来覆盖父类的实现,从而提供更好的灵活性。
4.4、工厂方法的参数和返回
工厂方法的实现中,可能需要参数,以便决定到底选用哪一种具体的实现。也就是说通过在抽象方法里面传递参数,在子类实现的时候根据参数进行选择,看看究竟应该创建哪一个具体的实现对象。
一般工厂方法返回的是被创建对象的接口对象,当然也可以是抽象类或者一个具体的类的实例。
4.5、谁来使用工厂方法创建的对象
这里首先要搞明白一件事情,就是谁在使用工厂方法创建的对象?
事实上,在工厂方法模式里面,应该是Creator中的其它方法在使用工厂方法创建的对象,虽然也可以把工厂方法创建的对象直接提供给Creator外部使用,但工厂方法模式的本意,是由Creator对象内部的方法来使用工厂方法创建的对象,也就是说,工厂方法一般不提供给Creator外部使用。
客户端应该是使用Creator对象,或者是使用由Creator创建出来的对象。对于客户端使用Creator对象,这个时候工厂方法创建的对象,是Creator中的某些方法使用。对于使用那些由Creator创建出来的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。分别举例来说明。
①客户端使用Creator对象的情况
比如《
工厂方法模式(Factory Method Pattern) 应用篇(上)
》的示例,对于“实现导出数据的业务功能对象”的类ExportOperate,它有一个export的方法,在这个方法里面,需要使用具体的“导出的文件对象的接口对象” ExportFileApi,而ExportOperate是不知道具体的ExportFileApi实现的,那么怎么做的呢?就是定义了一个工厂方法,用来返回ExportFileApi的对象,然后export方法会使用这个工厂方法来获取它所需要的对象,然后执行功能。
这个时候的客户端是怎么做的呢?这个时候客户端主要就是使用这个ExportOperate的实例来完成它想要完成的功能,也就是客户端使用Creator对象的情况
简单描述这种情况下的代码结构如下:
/*** 客户端使用Creator对象的情况下,Creator的基本实现结构*/public abstract class Creator {/*** 工厂方法,一般不对外* @return 创建的产品对象*/protected abstract Product factoryMethod ();/*** 提供给外部使用的方法,* 客户端一般使用Creator提供的这些方法来完成所需要的功能*/public void someOperation (){//在这里使用工厂方法Product p = factoryMethod();}}
②客户端使用由Creator创建出来的对象
另外一种是由Creator向客户端返回由“工厂方法创建的对象”来构建的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。简单描述这种情况下的代码结构如下:
/*** 客户端使用Creator来创建客户端需要的对象的情况下,Creator的基本实现结构*/public abstract class Creator {/*** 工厂方法,一般不对外,创建一个部件对象* @return 创建的产品对象,一般是另一个产品对象的部件*/protected abstract Product1 factoryMethod1 ();/*** 工厂方法,一般不对外,创建一个部件对象* @return 创建的产品对象,一般是另一个产品对象的部件*/protected abstract Product2 factoryMethod2 ();/*** 创建客户端需要的对象,客户端主要使用产品对象来完成所需要的功能* @return 客户端需要的对象*/public Product createProduct (){//在这里使用工厂方法,得到客户端所需对象的部件对象Product1 p1 = factoryMethod1();Product2 p2 = factoryMethod2();//工厂方法创建的对象是创建客户端对象所需要的Product p = new ConcreteProduct();p.setProduct1(p1);p.setProduct2(p2);return p;}}
在
工厂方法模式
里面,客户端要么使用
Creator对象
,要么使用
Creator创建的对象
,一般客户端不直接使用工厂方法。
4.6、工厂方法模式的调用顺序示意图
由于客户端使用Creator对象有两种典型的情况,因此调用的顺序示意图也分做两种情况,
先来看客户端使用Creator对象时候的调用顺序示意图,如图5所示:
图5 客户端使用Creator对象的调用顺序示意图
接着看看客户端使用由
Creator创建出来的对象
情况的调用顺序示意图,如图6所示:
图6
客户端使用由Creator创建出来的对象的调用顺序示意图
4.7、工厂方法模式和IoC/DI的关系
五、 使用示例
5.1、普通工厂方法模式的使用
5.2、参数化工厂方法模式的使用
所谓
参数化工厂方法
指的就是:通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象,这种情况就被称为参数化工厂方法。当然工厂方法创建的不同的产品必须是同一个Product类型的。
六、 工厂方法模式的优缺点
6.1、可以在不知具体实现的情况下编程
工厂方法模式可以让你在实现功能的时候,如果需要某个产品对象,只需要使用产品的接口即可,而无需关心具体的实现。选择具体实现的任务延迟到子类去完成。
6.2、更容易扩展对象的新版本
工厂方法给子类提供了一个挂钩,使得扩展新的对象版本变得非常容易。比如上面示例的参数化工厂方法实现中,扩展一个新的导出Xml文件格式的实现,已有的代码都不会改变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。
另外这里提到的挂钩,就是我们经常说的钩子方法(hook),这个会在后面讲模板方法模式的时候详细点说明。
6.3、连接平行的类层次
工厂方法除了创造产品对象外,在连接平行的类层次上也大显身手。这个在前面已经详细讲述了。
具体产品对象和工厂方法的耦合性
在工厂方法模式里面,工厂方法是需要创建产品对象的,也就是需要选择具体的产品对象,并创建它们的实例,因此具体产品对象和工厂方法是耦合的。
七、 思考工厂方法模式
7.1、工厂方法模式的本质
工厂方法模式的本质:延迟到子类来选择实现。
仔细体会前面的示例,你会发现,工厂方法模式中的工厂方法,在真正实现的时候,一般是先选择具体使用哪一个具体的产品实现对象,然后创建这个具体产品对象的示例,然后就可以返回去了。也就是说,工厂方法本身并不会去实现产品接口,具体的产品实现是已经写好了的,工厂方法只要去选择实现就好了。
有些朋友可能会说,这不是跟简单工厂一样吗?
确实从本质上讲,它们是非常类似的,具体实现上都是在“选择实现”。但是也存在不同点,简单工厂是直接在工厂类里面进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面使用工厂方法的地方是依赖于抽象而不是具体的实现,从而使得系统更加灵活,具有更好的可维护性和可扩展性。
其实如果把工厂模式中的Creator退化一下,只提供工厂方法,而且这些工厂方法还都提供默认的实现,那不就变成了简单工厂了吗?比如把
《
工厂方法模式(Factory Method Pattern) 应用篇(下)
》
的例子代码拿过来再简化一下,你就能看出来,写得跟简单工厂是差不多的,示例代码如下:
看完上述代码,会体会到简单工厂和工厂方法模式是有很大相似性的了吧,从某个角度来讲,可以认为简单工厂就是工厂方法模式的一种特例,因此它们的本质是类似的,也就不足为奇了。
7.2、对设计原则的体现
工厂方法模式很好的体现了“依赖倒置原则”。
依赖倒置原则告诉我们“要依赖抽象,不要依赖于具体类”,简单点说就是:不能让高层组件依赖于低层组件,而且不管高层组件还是低层组件,都应该依赖于抽象。
比如前面的示例,实现客户端请求操作的ExportOperate就是高层组件;而具体实现数据导出的对象就是低层组件,比如ExportTxtFile、ExportDB;而ExportFileApi接口就相当于是那个抽象。
对于ExportOperate来说,它不关心具体的实现方式,它只是“面向接口编程”;对于具体的实现来说,它只关心自己“如何实现接口”所要求的功能。
那么倒置的是什么呢?倒置的是这个接口的“所有权”。事实上,ExportFileApi接口中定义的功能,都是由高层组件ExportOperate来提出的要求,也就是说接口中的功能,是高层组件需要的功能。但是高层组件只是提出要求,并不关心如何实现,而低层组件,就是来真正实现高层组件所要求的接口功能的。因此看起来,低层实现的接口的所有权并不在底层组件手中,而是倒置到高层组件去了。
7.3、何时选用工厂方法模式
建议在如下情况中,选用工厂方法模式:
A
:如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类去实现
B
:如果一个类本身就希望,由它的子类来创建所需的对象的时候,应该使用工厂方法模式
八、 相关模式
工厂方法模式
和
抽象工厂模式
这两个模式可以组合使用,具体的放到抽象工厂模式中去讲。
工厂方法模式
和
模板方法模式
这两个模式外观类似,都是有一个抽象类,然后由子类来提供一些实现,但是工厂方法模式的子类专注的是创建产品对象,而模板方法模式的子类专注的是为固定的算法骨架提供某些步骤的实现。
这两个模式可以组合使用,通常在模板方法模式里面,使用工厂方法来创建模板方法需要的对象。
工厂方法模式(Factory Method Pattern)应用篇
Mediator模式也叫中介者模式,是由GoF提出的23种软件设计模式的一种。Mediator模式是行为模式之一,在Mediator模式中,类之间的交互行为被统一放在Mediator的对象中,对象通过Mediator对象同其他对象交互,Mediator对象起着控制器的作用。
关于 Mediator模式 的更多理论知识请参考《 设计模式之Mediator(中介者)模式(理论篇) 》
二、何时使用中介者模式
各个对象之间的交互操作非常多,每个对象的行为操作都依赖彼此对方,修改一个对象的行为,同时会涉及到修改很多其他对象的行为,如果使用Mediator模式,可以使各个对象间的耦合松散,只需关心和 Mediator的关系,使多对多的关系变成了一对多的关系,可以降低系统的复杂性,提高可修改扩展性。
在下列情况下使用中介者模式:
1、一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。
三、举例
Mediator模式是GoF的23种设计模式中较为容易理解的一个,我们在平时的应用中也会经常用到,但可能不会有意去抽象出一个完整的Mediator类,因为要设计一个可复用又可扩展的Mediator是很不容易的。
Mediator模式的主要作用在于为多个对象之间的交互提供一种媒介,以简化对象之间的耦合关系。下面用两个实例来说明Mediator模式的应用。
3.1、 ChatRoom(聊天室)示例
聊天室大家想必都知道,在下面的示例中,我们尝试实现一个布告栏式的ChatRoom,任何人想要发送消息给其他人,只需将消息发送给ChatRoom对象,由ChatRoom负责数据的转发,而不是直接与消息的接收者交互。 后面的笔记中将从Observer模式的角度来解决ChatRoom问题。
#include <iostream>
#include <string>
#include <map>
using namespace std ;
class Participant ;
// "AbstractMediator"
struct IChatroom
{
// Methods
virtual void Register ( Participant * participant ) = 0 ;
virtual void Send ( string & from , string & to , string & message ) = 0 ;
};
// "AbstractColleague"
class Participant
{
friend class Chatroom ;
// Fields
private :
IChatroom * pChatroom ;
string name ;
// Constructors
public :
/*Participant()
{
}*/
Participant ( const char * name )
{
this -> name = name ;
}
virtual ~ Participant ()
{
}
// Methods
virtual void Send ( string & to , string & message )
{
pChatroom -> Send ( name , to , message );
}
virtual void Receive ( string & from , string & message )
{
cout << from . c_str () << " to " << name . c_str () << " : [" << message . c_str () << "]" << endl ;
}
};
// More ConcreteColleague, omitted...
// "ConcreteMediator"
class Chatroom : public IChatroom
{
// Fields
private :
map < string , Participant *> mapParticipants ; // nickname to Participant map
// Methods
public :
void Register ( Participant * pParticipant )
{
mapParticipants [ pParticipant -> name ] = pParticipant ;
pParticipant -> pChatroom = this ;
}
void Send ( string & from , string & to , string & message )
{
map < string , Participant *>:: iterator ptr ;
ptr = mapParticipants . find ( to );
if ( ptr != mapParticipants . end () )
{
Participant * pto = (* ptr ). second ;
pto -> Receive ( from , message );
}
}
};
int main ()
{
// Create chatroom
Chatroom c ;
// Create 'chatters' and register them
Participant George ( "George" );
Participant Paul ( "Paul" );
Participant Ringo ( "Ringo" );
c . Register ( & George );
c . Register ( & Paul );
c . Register ( & Ringo );
// Chatting participants
George . Send ( string ( "Paul" ), string ( "Hi Paul!" ) );
Paul . Send ( string ( "Ringo" ), string ( "Good Morning!" ) );
Ringo . Send ( string ( "George" ), string ( "Hi Friend!" ) );
return 0 ;
}
3.2、多线程Producer-Consumer(生产者和消费者示例)
以下是一个多线程Producer-Comsumer的例子,在该示例中,由于无法在多个Producer、Cosumer之间建立直接的联系,因此,通过Mediator类来完成这种信息交互,当Producer要Produce时,只需与Mediator进行交互,以查询是否有空的Slot可供存放Product,而Comsumer要Comsume时,也只需与Mediator进行交互,以查询是否有Product可供Comsume。
以下是该示例的Java实现:
import java . util .*;
class Product {
int id ;
Product ( int id ) {
this . id = id ;
}
}
class Mediator {
private boolean stopFlag = false;
private Stack slot = new Stack ();
private int slotCount ;
public Mediator ( int slotCount ) {
this . slotCount = slotCount ;
}
public boolean stop () {
return stopFlag ;
}
public void stop ( boolean flag ) {
stopFlag = true;
}
public boolean put ( Product product ) {
synchronized ( slot ) { // or synchronized on Mediator.class, but on slot is better and reasonable
if ( slot . size () >= slotCount ) {
return false;
}
slot . push ( product );
}
return true;
}
public Product get () {
synchronized ( slot ) {
if ( slot . empty () )
return null ;
Product product = ( Product ) slot . pop ();
return product ;
}
}
}
class Producer extends Thread {
private Mediator med ;
private int id ;
private static int num = 1 ;
public Producer ( Mediator m ) {
med = m ;
id = num ++;
}
public void run () {
Product product ;
while ( ! med . stop () ) {
product = new Product (( int ) ( Math . random () * 100 ));
synchronized ( System . out ) {
System . out . println ( "Producer[" + id + "] produces Product["
+ product . id + "]" );
}
while ( ! med . stop () && ! med . put ( product ) ) { // if put failed, try to put again and again.
try {
sleep ( 100 );
} catch ( InterruptedException ie ) {
}
}
try {
sleep ( 100 );
} catch ( InterruptedException ie ) {
}
}
}
}
class Consumer extends Thread {
private Mediator med ;
private int id ;
private static int num = 1 ;
public Consumer ( Mediator m ) {
med = m ;
id = num ++;
}
public void run () {
Product product ;
while ( ! med . stop () ) {
product = med . get ();
if ( product != null ) {
synchronized ( System . out ) {
System . out . println ( "Consumer[" + id + "] is consuming Product["
+ product . id + "]" );
}
}
try {
sleep ( 100 );
} catch ( InterruptedException ie ) {
}
}
}
}
class MediatorDemo {
public static void main ( String [] args ) {
Mediator med = new Mediator ( 2 );
Thread thread [] = { new Producer ( med ), new Producer ( med ),
new Consumer ( med ), new Consumer ( med ), new Consumer ( med ) };
for ( int i = 0 ; i < thread . length ; i ++)
thread [ i ]. start ();
// before stop all threads, sleep 1 second
try {
Thread . sleep ( 1000 );
} catch ( InterruptedException ie ) {
}
med . stop (true);
// Wait for all threads to return
try {
for ( int i = 0 ; i < thread . length ; i ++) {
thread [ i ]. join ();
}
} catch ( InterruptedException ie ) {
}
}
}
工厂方法模式(Factory Method Pattern)应用篇(直接使用工厂方法)
一、什么是多态工厂模式
多态工厂模式
属于类的创建型模式,它又被称为
工厂方法模式
(
Factory Method
Pattern
)
。
多态工厂模式
的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
二、模式中包含的角色及其职责
1.
抽象工厂(Creator)角色: (比如
示例1中的FruitFactory.java) 工厂方法模式的核心,任何工厂类都必须实现这个接口。
2.
具体工厂( Concrete Creator):角色 (比如
示例1
中的PearFactory.java) 具体工厂类是抽象工厂的一个实现,负责实例化产品对象。
3.
抽象(Product)角色 :(比如
示例1
中的Fruit.java)工厂方法模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
4.
具体产品(Concrete Product)角色: (比如
示例1
中的Apple.java, Banana.java, Pear.java)工厂方法模式所创建的具体实例对象
三、 多态工厂模式 和简单工厂模式比较
多态工厂模式
与
简单工厂模式
在结构上的不同不是很明显。
多态工厂模式
的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。
工厂方法模式
之所以有一个别名叫
多态性工厂模式
是因为具体工厂类都有共同的接口,或者有共同的抽象父类。 当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了“开放-封闭”原则。而
简单工厂模式
在添加新产品对象后不得不修改工厂方法,扩展性不好。工厂方法模式退化后可以演变成简单工厂模式。
四、多态使用实例
在工厂方法模式里面,应该是
Creator
中的其它方法在使用工厂方法创建的对象,虽然也可以把工厂方法创建的对象直接提供给Creator外部使用,但工厂方法模式的本意,是由Creator对象内部的方法来使用工厂方法创建的对象,也就是说,工厂方法一般不提供给Creator外部使用。
客户端
应该是使用Creator对象,或者是使用由
Creator
创建出来的对象。对于客户端使用
Creator对象
,这个时候工厂方法创建的对象,是Creator中的某些方法使用。对于使用那些由
Creator创建出来的对象
,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。
工厂方法一般不提供给Creator外部使用,但是有时我们的确需要这么做。下面将举例来说明。
示例1
Fruit.java文件
public interface Fruit {/** 采集*/public void get ();}
Apple.java文件
public class Apple implements Fruit {/** 采集*/public void get (){System . out . println ( "采集苹果" );}}
Banana.java文件
public interface FruitFactory {public Fruit getFruit ();}
Pear.java文件
public class Pear implements Fruit {public void get () {System . out . println ( "采集梨子" );}}
FruitFactory.java
public interface FruitFactory {public Fruit getFruit ();}
AppleFactory.java文件
public class AppleFactory implements FruitFactory {public Fruit getFruit () {return new Apple ();}}
BananaFactory.java文件
public class BananaFactory implements FruitFactory {public Fruit getFruit () {return new Banana ();}}
PearFactory.java文件
public class PearFactory implements FruitFactory {public Fruit getFruit () {return new Pear ();}}
MainClass.java文件
public class MainClass {public static void main ( String [] args ) {//获得AppleFactoryFruitFactory ff = new AppleFactory();//通过AppleFactory来获得Apple实例对象Fruit apple = ff.getFruit();apple.get();//获得BananaFactoryFruitFactory ff2 = new BananaFactory();Fruit banana = ff2.getFruit();banana.get();//获得PearFactoryFruitFactory ff3 = new PearFactory();Fruit pear = ff3.getFruit();pear.get();}}
工厂方法模式(Factory Method Pattern) 应用篇(上)
一、前言
工厂方法模式(Factory Method Pattern)
属于类的创建型模式,它又被称为
多态工厂模式
。
二、场景
考虑这样一个实际应用:实现一个
导出数据的应用框架
,来让客户选择数据的导出方式,并真正执行数据导出。
在一些实际的企业应用中,一个公司的系统往往分散在很多个不同的地方运行,比如各个分公司或者是门市点,公司没有建立全公司专网的实力,但是又不愿意让业务数据实时的在广域网上传递,一个是考虑数据安全的问题,一个是运行速度的问题。
这种系统通常会有一个折中的方案,那就是各个分公司内运行系统的时候是独立的,是在自己分公司的局域网内运行。然后在每天业务结束的时候,各个分公司会导出自己的业务数据,然后把业务数据打包通过网络传送给总公司,或是专人把数据送到总公司,然后由总公司进行数据导入和核算。
通常这种系统,在导出数据上,会有一些约定的方式,比如导出成:文本格式、数据库备份形式、Excel格式、Xml格式等等。
现在就来考虑实现上文中提到的
导出数据的应用框架
。
分析上面要实现的应用框架,不管用户选择什么样的导出格式,最后导出的都是一个文件,而且系统并不知道究竟要导出成为什么样的文件,因此应该有一个统一的接口,来描述系统最后生成的对象,并操作输出的文件。
先把导出的文件对象的接口定义出来,示例代码如下:
/** * 导出的文件对象的接口*/public interface ExportFileApi {/*** 导出内容成为文件* @param data 示意:需要保存的数据* @return 是否导出成功*/public boolean export ( String data );}
对于实现导出数据的业务功能对象,它应该根据需要来创建相应的ExportFileApi的实现对象,因为特定的ExportFileApi的实现是与具体的业务相关的。但是对于实现导出数据的业务功能对象而言,它并不知道应该创建哪一个ExportFileApi的实现对象,也不知道如何创建。
也就是说:
对于实现导出数据的
业务功能对象
,它需要创建ExportFileApi的具体实例对象,但是它只知道ExportFileApi接口,而不知道其具体的实现。那该怎么办呢
?
四、 使用工厂方法模式来实现示例
要使用
工厂方法模式
来实现示例,先来按照工厂方法模式的结构,对应出哪些是被创建的Product,哪些是Creator。分析要求实现的功能,导出的文件对象接口ExportFileApi就相当于是Product,而用来实现导出数据的业务功能对象就相当于Creator。把Product和Creator分开过后,就可以分别来实现它们了。
使用工厂模式来实现示例的程序结构如
图4所示:
图4 使用工厂模式来实现示例的程序结构示意图
下面一起来看看代码实现。
(1)
导出的文件对象接口ExportFileApi的实现没有变化,这里就不去赘述了
(2)
接下来看看接口ExportFileApi的实现,为了示例简单,只实现导出文本文件格式和数据库备份文件两种。先看看导出文本文件格式的实现,示例代码如下:
/**
* 导出成文本文件格式的对象*/public class ExportTxtFile implements ExportFileApi{public boolean export(String data) {//简单示意一下,这里需要操作文件System.out.println("导出数据"+data+"到文本文件");return true;}}
再看看导出成数据库备份文件形式的对象的实现,示例代码如下:
/*** 导出成数据库备份文件形式的对象*/public class ExportDB implements ExportFileApi {public boolean export ( String data ) {//简单示意一下,这里需要操作数据库和文件System.out.println("导出数据"+data+"到数据库备份文件");return true;}}
(3)
Creator这边的实现,首先看看ExportOperate的实现,示例代码如下:
/*** 实现导出数据的业务功能对象*/public abstract class ExportOperate {/*** 导出文件* @param data 需要保存的数据* @return 是否成功导出文件*/public boolean export ( String data ){//使用工厂方法ExportFileApi api = factoryMethod();return api.export(data);}/*** 工厂方法,创建导出的文件对象的接口对象* @return 导出的文件对象的接口对象*/protected abstract ExportFileApi factoryMethod();}
(4)
加入了两个Creator实现,先看看创建导出成文本文件格式的对象,示例代码如下:
/*** 具体的创建器实现对象,实现创建导出成文本文件格式的对象*/public class ExportTxtFileOperate extends ExportOperate {protected ExportFileApi factoryMethod () {//创建导出成文本文件格式的对象return new ExportTxtFile();}}
再看看创建导出成数据库备份文件形式的对象,示例代码如下:
/*** 具体的创建器实现对象,实现创建导出成数据库备份文件形式的对象*/public class ExportDBOperate extends ExportOperate {protected ExportFileApi factoryMethod () {//创建导出成数据库备份文件形式的对象return new ExportDB();}}
(5)
客户端直接创建需要使用的Creator对象,然后调用相应的功能方法,示例代码如下:
public class Client {public static void main ( String [] args ) {//创建需要使用的Creator对象ExportOperate operate = new ExportDBOperate();//调用输出数据的功能方法operate.export("测试数据");}}
运行结果如下:
导出数据测试数据到数据库备份文件
你还可以修改客户端new的对象,切换成其它的实现对象,试试看会发生什么。看来应用工厂方法模式是很简单的,对吧。
工厂方法模式(Factory Method Pattern) 应用篇(下)
五、 使用工厂方法模式来实现
所谓
参数化工厂方法
指的就是:通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象,这种情况就被称为参数化工厂方法。当然工厂方法创建的不同的产品必须是同一个Product类型的。
来改造前面的示例,现在有一个工厂方法来创建ExportFileApi这个产品的对象,但是ExportFileApi接口的具体实现很多,为了方便创建的选择,直接从客户端传入一个参数,这样在需要创建ExportFileApi对象的时候,就把这个参数传递给工厂方法,让工厂方法来实例化具体的ExportFileApi实现对象。
还是看看代码示例会比较清楚。
(1)先来看Product的接口,就是ExportFileApi接口,跟前面的示例没有任何变化,为了方便大家查看,这里重复一下,示例代码如下:
/*** 导出的文件对象的接口*/public interface ExportFileApi {/*** 导出内容成为文件* @param data 示意:需要保存的数据* @return 是否导出成功*/public boolean export ( String data );}
(2)同样提供保存成文本文件和保存成数据库备份文件的实现,跟前面的示例没有任何变化,示例代码如下:
public class ExportTxtFile implements ExportFileApi {public boolean export ( String data ) {//简单示意一下,这里需要操作文件System.out.println("导出数据"+data+"到文本文件");return true;}}
public class ExportDB implements ExportFileApi {public boolean export ( String data ) {//简单示意一下,这里需要操作数据库和文件System.out.println("导出数据"+data+"到数据库备份文件");return true;}}
(3)接下来该看看ExportOperate类了,这个类的变化大致如下:
ExportOperate类中的创建产品的工厂方法,通常需要提供默认的实现,不抽象了,也就是变成正常方法
ExportOperate类也不再定义成抽象类了,因为有了默认的实现,客户端可能需要直接使用这个对象
设置一个导出类型的参数,通过export方法从客户端传入
看看代码吧,示例代码如下:
/*** 实现导出数据的业务功能对象*/public class ExportOperate {/*** 导出文件* @param type 用户选择的导出类型* @param data 需要保存的数据* @return 是否成功导出文件*/public boolean export ( int type , String data ){//使用工厂方法ExportFileApi api = factoryMethod(type);return api.export(data);}/*** 工厂方法,创建导出的文件对象的接口对象* @param type 用户选择的导出类型* @return 导出的文件对象的接口对象*/protected ExportFileApi factoryMethod(int type){ExportFileApi api = null;//根据类型来选择究竟要创建哪一种导出文件对象if(type==1){api = new ExportTxtFile();}else if(type==2){api = new ExportDB();}return api;}}
(4)此时的客户端,非常简单,直接使用ExportOperate类,示例代码如下:
public class Client {public static void main ( String [] args ) {//创建需要使用的Creator对象ExportOperate operate = new ExportOperate();//调用输出数据的功能方法,传入选择到处类型的参数operate.export(1,"测试数据");}}
测试看看,然后修改一下客户端的参数,体会一下通过参数来选择具体的导出实现的过程。这是一种很常见的参数化工厂方法的实现方式,但是也还是有把参数化工厂方法实现成为抽象的,这点要注意,并不是说参数化工厂方法就不能实现成为抽象类了。只是一般情况下,参数化工厂方法,在父类都会提供默认的实现。
(5)扩展新的实现
使用参数化工厂方法,扩展起来会非常容易,已有的代码都不会改变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。
这种实现方式还有一个有意思的功能,就是子类可以选择性覆盖,不想覆盖的功能还可以返回去让父类来实现,很有意思。
先扩展一个导出成xml文件的实现,试试看,示例代码如下:
/*** 导出成xml文件的对象*/public class ExportXml implements ExportFileApi {public boolean export ( String data ) {//简单示意一下System.out.println("导出数据"+data+"到XML文件");return true;}}
然后扩展ExportOperate类,来加入新的实现,示例代码如下:
/*** 扩展ExportOperate对象,加入可以导出XML文件*/public class ExportOperate2 extends ExportOperate {/*** 覆盖父类的工厂方法,创建导出的文件对象的接口对象* @param type 用户选择的导出类型* @return 导出的文件对象的接口对象*/protected ExportFileApi factoryMethod ( int type ){ExportFileApi api = null ;//可以全部覆盖,也可以选择自己感兴趣的覆盖,//这里只想添加自己新的实现,其它的不管if(type==3){api = new ExportXml();}else{//其它的还是让父类来实现api = super.factoryMethod(type);}return api;}}
看看此时的客户端,也非常简单,只是在变换传入的参数,示例代码如下:
public class Client {public static void main ( String [] args ) {//创建需要使用的Creator对象ExportOperate operate = new ExportOperate2();//下面变换传入的参数来测试参数化工厂方法operate.export(1,"Test1");operate.export(2,"Test2");operate.export(3,"Test3");}}
对应的测试结果如下:
导出数据Test1到文本文件
导出数据Test2到数据库备份文件
导出数据Test3到XML文件
通过上面的示例,好好体会一下参数化工厂方法的实现和带来的好处。
简单工厂模式 Simple Factory Pattern
一、什么是简单工厂模式
简单工厂模式
(
Simple Factory Pattern
)属于类的创建型模式,又叫做
静态工厂方法模式
。通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
二、模式中包含的角色及其职责
1.
工厂(Creator)角色: 简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
2.
抽象(Product)角色: 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
3.
具体产品(Concrete Product)角色: 简单工厂模式所创建的具体实例对象
三、简单工厂模式的优缺点
在这个模式中,工厂类是整个模式的关键所在。它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。
优点如下:
用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。
缺点如下:
由于工厂类集中了所有实例的创建逻辑,所以“高内聚”方面做的并不好。另外,当系统中的具体产品类不断增多时,可能会出现要求工厂类也要做相应的修改,扩展性并不很好。
:示例1:
Fruit.java文件
public interface Fruit {/** 采集*/public void get ();}
Apple.java文件
public class Apple implements Fruit {/** 采集*/public void get (){System . out . println ( "采集苹果" );}}
Banana.java文件
public class Banana implements Fruit {/** 采集*/public void get (){System . out . println ( "采集香蕉" );}}
FruitFactory.java文件
public class FruitFactory {// /*// * 获得Apple类的实例// */// public static Fruit getApple() {// return new Apple();// }//// /*// * 获得Banana类实例// */// public static Fruit getBanana() {// return new Banana();// }/** get方法,获得所有产品对象*/public static Fruit getFruit(String type) throws InstantiationException, IllegalAccessException, ClassNotFoundException {// if(type.equalsIgnoreCase("apple")) {// return Apple.class.newInstance();//// } else if(type.equalsIgnoreCase("banana")) {// return Banana.class.newInstance();// } else {// System.out.println("找不到相应的实例化类");// return null;// }Class fruit = Class.forName(type);return (Fruit) fruit.newInstance();}}
MainClass.java文件
public class MainClass {public static void main ( String [] args ) throws InstantiationException , IllegalAccessException , ClassNotFoundException {// //实例化一个Apple// Apple apple = new Apple();// //实例化一个Banana// Banana banana = new Banana();//// apple.get();// banana.get();// //实例化一个Apple,用到了多态// Fruit apple = new Apple();// Fruit banana = new Banana();// apple.get();// banana.get();// //实例化一个Apple// Fruit apple = FruitFactory.getApple();// Fruit banana = FruitFactory.getBanana();// apple.get();// banana.get();Fruit apple = FruitFactory.getFruit("Apple");Fruit banana = FruitFactory.getFruit("Banana");apple.get();banana.get();}}