设计模式三(简单工厂模式的升级-工厂方法模式)

简介

上一篇我们已经说了,简单工厂模式有一个天生的弊端,即所有的产品都出自一个工厂类,而且在创建产品的时候,必须以参数的形式告诉工厂我要创建哪个产品。这种设计在产品类比较少,而且产品种类更改不频繁的时候,勉强还可以应付,但是我们可以试想假如我们的产品类很多,有成百上千个,并且后前还会逐步添加,而且暂时还不知道要添加什么产品,这样我们的工厂类应该怎么写?要写成百上千个if else吗?要在每次添加产品的时候去修改工厂类吗?这样显然是一种很恶心的模式,所以我们的工厂方法模式就应运而生。

概念

工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

可以看到工厂方法模式中定义了一个工厂接口,而具体的创建工作推迟到具体的工厂类,它是对简单工厂模式中的工厂类进一步抽象化,从而产生一个工厂类的抽象和实现体系,从而弥补简单工厂模式对修改开放的诟病。

类图

2017-11-16_115826.png

可以看到,上面右半部分是产品抽象和实现体系,左半部分是工厂抽象和实现体系,其中工厂体系依赖于产品体系,每一个工厂负责创造一种产品,这就省去了简单工厂中的elseif判断,又客户端决定实例化一个特定的工厂去创建相应的产品。

角色

抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

案例演示

1、一个产品接口

/**
 * 一个产品的抽象接口(定义产品共性)
 */
public interface IProduct {
    public void action();
}

2、若干个产品类

/**
 * 产品一:汽车
 */
public class Car implements IProduct {
    public void action() {
        System.out.println("汽车在路上跑!");  
    }
}

/**
 * 产品二:飞机
 */
public class Airplane implements IProduct {
    public void action() {
        System.out.println("飞机在天上飞!");      
    }
}

3、一个工厂接口

/**
 * 一个工厂接口(用于抽象所有的接口特性)
 */
public interface Factory {
    public IProduct createProduct();
}

4、若干个工厂类

/**
 * 产品工厂类一:汽车工厂
 */
public class FactoryCar implements Factory {
    public IProduct createProduct() { 
        return new Car();
    }
}

/**
 * 产品工厂二:飞机工厂
 */
public class FactoryAirplane implements Factory {
    public IProduct createProduct() { 
        return new Airplane();
    }
}

5、测试类

public class App {
    public static void main(String[] args) {
        //创建一个汽车工厂
        Factory factory = new FactoryCar();
        Car car = (Car) factory.createProduct();
        car.action();
        
        //创建一个飞机工厂
        factory = new FactoryAirplane();
        Airplane airplane = (Airplane) factory.createProduct();
        airplane.action();      
    }
}
2017-11-16_124221.png
2017-11-17_150757.png

下面看看简单工厂与工厂方法的对比


2017-11-17_151148.png

我们可以看出工厂方法与简单方法唯一的不同之处就在于工厂创建模块,简单工厂是直接一个工厂类创建了所有的产品,产品固定且少的时候适宜用简单工厂模式;而工厂方法模式只是在简单工厂模式的基础上对工厂进行了抽象,让各个产品的生产对应相应的工厂,这样在有新产品需要添加时,只需要开发一个对应的产品和产品工厂,无需对原有的其他代码进行改动。

开发中的案例

不知大家在开发的时候注意到在链接数据库JDBC的API设计模式没有。

众所周知,为了统一各个数据库操作的标准,于是有了JDBC的API,它用于给我们这种被称作只会使用现成的东西的程序猿,提供一系列统一的,标准化的操作数据库的接口。其实JDBC的各个类或接口,就是我们操作数据库的过程中各个协助者的抽象,这样的设计是为了让我们对数据库的操作依赖于抽象,还记得我们在设计模式总纲中提到的一句话吗,用抽象构建框架,用细节扩展实现。

JDBC API(即抽象的接口或类)就是整个数据库操作的框架,而各个数据库的驱动就是那些细节。而我们的操作依赖于JDBC API,而不是任何一个具体数据库的细节。

JDBC是如何统一了数据库世界的呢?其实最主要的就是靠两个接口,就统一了世界。。

来看第一个接口Driver源码

package java.sql;

import java.sql.DriverPropertyInfo;
import java.sql.SQLException;

/**
 * The interface that every driver class must implement.
 */
public interface Driver {

    Connection connect(String url, java.util.Properties info)
        throws SQLException;

    boolean acceptsURL(String url) throws SQLException;

    DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
             throws SQLException;

    int getMajorVersion();

    int getMinorVersion();

    boolean jdbcCompliant();
} 

我们可以看到,这个接口中的connect方法,获取数据库连接。翻译过来是这是一个任何驱动类都必须实现的接口。多么霸气啊。也就是每个数据库厂商都必须实现这个接口来提供JDBC服务,即java数据库连接服务,来方便程序猿对数据库应用编程。

我们先忽略掉下面的五个方法,第一个方法毫无疑问是这个接口中相对而讲最重要的方法了,即创造一个数据库连接,虽然方法名称是connect,但是我觉得这个方法完全可以改为createConnection。

提到Connction,这个接口我们一定不陌生,我们截取它的一部分源码

package java.sql;

import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * <P>A connection (session) with a specific
 * database. SQL statements are executed and results are returned
 * within the context of a connection.
 * <P>
 */
public interface Connection  extends Wrapper {

    Statement createStatement() throws SQLException;

    PreparedStatement prepareStatement(String sql) throws SQLException;

}

以上便是Connection接口,这里只留下了两个方法,这两个方法相信各位读者都非常熟悉,它们都是我们最经常用的方法之二。

以上两个接口作为JDBC API的一部分,它们相当于告诉了数据库生产厂商两个要求。

第一,数据库厂商要提供一个数据库驱动类,它的作用可以是可以创造数据库连接,而这个数据库连接向上转型为我们JDBC的Connection。
第二,数据库厂商要提供一个数据库连接的实现类,这个实现类可以执行具体数据库的各个操作,比如帮我们执行SQL,返回执行结果,关闭连接等等。

我们都知道mysql的驱动类位于com.mysql.jdbc.Driver,而mysql的connection实现类也在这个包中,名称是ConnectionImpl,而相应的oracle也有驱动类,位于oracle.jdbc.driver.OracleDriver,相应的oracle也有connection实现类,位于oracle.jdbc.OracleConnectionWrapper。一般每个数据库都会有一个Connection的扩展接口,这个接口的作用是提供使用者针对当前数据库特殊的操作。

我们来看一个类图吧

2017-11-17_152558.png

我们对比上面标准的工厂方法模式,就会发现它们的关系不正是工厂方法模式吗?

工厂方法模式就是提供一个抽象的工厂,一个抽象的产品,在上述当中相当于Driver(数据库连接工厂)和Connection(抽象产品),实现的一方需要提供一个具体的工厂类(比如mysql驱动)和一个具体的产品(比如mysql数据库连接)。

工厂方法模式的好处和适用的场景都相对比较好理解。

从类关系上来说,它可以让客户端与具体的工厂与产品解耦,从业务角度来说,它让客户端与具体的产品解耦。
适用的场景就是我们需要一个产品帮我们完成一项任务,但是这个产品有可能有很多品牌(像这里的mysql,oracle),为了保持我们对产品操作的一致性,我们就可能要用到工厂方法模式。

另外在附上另一个java中有关工厂方法模式的案例

2017-11-17_153016.png

是不是很明显(上图来自于百度)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容