(四) 工厂方法模式

转载: http://www.cnblogs.com/zuoxiaolong/p/pattern5.html

本章我们继续讨论新的设计模式,工厂方式模式,在这之前,LZ决定先给出引自其它地方的标准定义以及类图。

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

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

下面LZ给出工厂方法模式的类图,该类图和定义引自百度百科。

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

下面LZ简单的使用JAVA代码诠释上述标准的工厂方法模式的类图。

首先是抽象产品接口。

public interface Light {

    public void turnOn();

    public void turnOff();

}

下面是具体的产品。

public class BuldLight implements Light{

    public void turnOn() {
        System.out.println("BuldLight On");
    }

    public void turnOff() {
        System.out.println("BuldLight Off");
    }

}

public class TubeLight implements Light{

    public void turnOn() {
        System.out.println("TubeLight On");
    }

    public void turnOff() {
        System.out.println("TubeLight Off");
    }

}

下面是抽象的工厂接口。

public interface Creator {

    public Light createLight();
}

下面是创建指定产品的具体工厂。

public class BuldCreator implements Creator{

    public Light createLight() {
        return new BuldLight();
    }

}

public class TubeCreator implements Creator{

    public Light createLight() {
        return new TubeLight();
    }

}

下面我们写个测试类去实验一下这个工厂方法模式的实例代码。

public class Client {

    public static void main(String[] args) {
        Creator creator = new BuldCreator();
        Light light = creator.createLight();
        light.turnOn();
        light.turnOff();

        creator = new TubeCreator();
        light = creator.createLight();
        light.turnOn();
        light.turnOff();
    }
}

运行结果如下。

可以看到,我们使用可以随意的在具体的工厂和产品之间切换,并且不需要修改任何代码,就可以让原来的程序正常运行,这也是工厂方法模式对扩展开放的表现,另外工厂方法模式弥补了简单工厂模式不满足开闭原则的诟病,当我们需要增加产品时,只需要增加相应的产品和工厂类,而不需要修改现有的代码。
              上面的示例可以比较清楚的展示各个类之间的关系,但是始终缺乏说服力,因为它完全没有什么实际意义,下面LZ就给出一些我们接触过的例子来说明工厂方法模式的好处。
               关于能够说明工厂方法模式的实例,LZ翻遍了所有能找到的源码,想寻找一个让各位读者既能学习到新的东西,又能对工厂方法理解更深的现有的优秀框架的设计。经过跋山涉水,LZ决定还是拿数据库连接来说事,我知道你想说,我去,又是数据库连接。LZ只想说,我们每天做的最多的就是增删改查好吗,其它的咱也不认识啊,囧。

众所周知,为了统一各个数据库操作的标准,于是有了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();
} 

由于篇幅,LZ删掉了很多注释,只保留了这个类注释的第一句话,翻译过来是这是一个任何驱动类都必须实现的接口。多么霸气啊。也就是每个数据库厂商都必须实现这个接口来提供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的扩展接口,这个接口的作用是提供使用者针对当前数据库特殊的操作。

这里我们忽略掉这些中间接口以及抽象类,我给出上述六个类的UML图,如果各位以前知道工厂方法模式的话,各位看一下,它们的关系是否很熟悉。

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

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

客户端调用时不依赖于具体工厂和产品(即到底是mysql驱动,mysql数据库连接还是oracle驱动,oracle连接,我们程序猿不需要管的,我们只管使用抽象的driver和connection,对吧?),而是依赖于抽象工厂和抽象产品完成工作。

各位可以看到我在类图里面加入了一个DriverManager,这个类相信各位也不陌生,这是我们天天打交道的类,虽说因为hibernate和ibatis的封装,或许我们不能经常看到,但LZ相信它活在每个程序猿的心中。

DriverMananger在这个设计当中扮演者一个管理者的角色,它帮我们管理数据库驱动,让我们不需要直接接触驱动接口,我们获取连接只需要和DriverManager打交道就可以,也就是说客户端依赖于DriverManager和Connection就可以完成工作,不再需要与Driver关联,所以上述说我们依赖于Driver和Connection,现在DriverManager帮我们管理Driver,那我们只需要依赖于DriverManager和Connection就可以了。

LZ在类图中拉出了DriverManager的方法,其中的registerDriver方法正是我们注册数据库驱动的入口。来看看mysql的Driver中做了什么,oracle类似。

public class Driver extends NonRegisteringDriver
  implements java.sql.Driver
{
  public Driver()
    throws SQLException
  {
  }

  static
  {
    try
    {
      DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
      throw new RuntimeException("Can‘t register driver!");
    }
  }
}

可以看到,在类构造方法中,加入了registerDriver这个方法,所以当我们使用class.forName加载驱动的时候,将会把mysql驱动注册到DriverManager,这时DriverManager中就会持有Mysql驱动所必要的信息,我们就可以使用DriverManager来获得具体的mysql连接了,当然,你要提供url,用户名和密码。

原来我们都是活在温室里的花朵,都被这些设计者细心呵护着,生怕我们知道一点底层的东西。记得LZ当初第一次看到Class.forName时,还觉得真是个神奇的东西,没想到只是这些设计者给我们的糖外衣。

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

好处就是,从类关系上来说,它可以让客户端与具体的工厂与产品解耦,从业务角度来说,它让客户端与具体的产品解耦

适用的场景就是我们需要一个产品帮我们完成一项任务,但是这个产品有可能有很多品牌(像这里的mysql,oracle),为了保持我们对产品操作的一致性,我们就可能要用到工厂方法模式。

工厂方法模式也有它所不足的地方,可能你会说,这多好啊,我们操纵数据库不再需要关心具体是哪个数据库。是的,你很爽啊,那是因为这些产品的实现都不用你写啊,都是数据库厂商给你写的。

假设产品数量巨多,而且需要我们亲手去逐个实现的时候,工厂方法模式就会增加系统的复杂性,到处都是工厂类和产品类,而且这里所说的工厂类和产品类只是概念上的,真正的产品可能不是一两个类就能搞定,否则mysql和oracle的驱动包为啥要那么多类,而不是就一个Driver和一个Connection。

当然这也不是绝对,比如我们经常使用的HashSet和ArrayList,也是使用的工厂方法模式,各位看下他们的类图就看出来了。

各位可能会说,不对啊,这和我们刚才理解的不太一样啊,按照刚才的说法,我们不是应该直接使用iterable和iterator吗?这样多牛X,我们不依赖于具体产品了。对于这个LZ表示三条黑线垂下,sun或者说oracle为了集合框架给你提供了这么多具备各个特性的集合,你只用iterator和iterable,估计当初参与设计集合框架的人都要气的去shi了。。

上述这便是工厂方法模式另外一种用法了,刚才因为我们不关心真正的产品是什么,所以我们直接使用抽象接口操作。但是我们使用iterable和iterator的时候,我们是关心真正产品的特性的,所以为了使用产品的特性,我们就需要使用产品特有的接口了,比如特殊的SortedSet可排序,比如ArrayList可以有重复元素,可以根据索引获取元素等等。当然你依然是可以使用iterable和iterator的,但是不管你用什么,在这种场景下,产品是你自己选的,一句话,你随便。。。

两种使用方式一种是对使用者透明的,一种是不透明的,一种是使用者对具体的产品不关心,这种情况下,一般产品提供的功能是类似的。一种是使用者非常了解产品的特性,并想使用产品的特性,这种情况下,一般产品只提供最基本的一致的功能,但每个产品都会有自己独特的一面。

但是LZ个人觉得真正做项目的过程当中很少用到工厂方法模式,这个模式更多的是帮助我们理解现有的开源项目,就像现在,你是不是对JDBC的大体框架有了一定认识了呢,如果你不知道这个模式,可能看源码会觉得一头雾水呢。

另外,文章最后插播一段内容,如果各位看过上一章(简单工厂模式)的话,一定还记得那个恶心的elseif结构,这是简单工厂的诟病,它对扩展开放,对修改也开放。

简单工厂模式在项目规模相对较小或者说具体的产品类相对不多的情况下(针对上章的描述,特指的servlet数量不多的情况下),其实这种设计还是可以接受的,因为少量的elseif可以换来我们开发上的便利。

所以LZ建议各位永远不要忘记,规则只是用来指导你的,不是用来限制你的,只要设计合理,你的设计就是规则

不过针对简单工厂模式,你可以认为它给我们提供了一个思路,就是我们其实可以省掉那些让人痛恨的xml配置,对于我们后续的优化有着一定指导意义。

就像上一章中的处理方式,很明显存在着隐患,那就是在servlet数量急剧上升的时候,工厂类就会变得非常臃肿和复杂,变得难以维护和阅读。本章LZ给各位读者介绍一种优化方式,可以采取一项JDK当中在1.5版本引入的技术,即注解,去消除那些elseif的逻辑判断。

我们可以参考struts2的做法,即每一个Servlet我们都可以采用注解去设置它的名称,或者叫url,然后我们让我们的简单工厂依据这个去实例化我们的servlet。

根据以上方案,我们需要按照以下步骤让我们的简单工厂彻底死翘翘。

1.需要声明一个注解,它可以用来给servlet标识它的名称。

               2.需要声明一个注解的处理器,用来处理我们的注解,主要作用是通过一个CLASS文件,去获得它的注解信息。

               3.基于性能,我们需要将servlet与名称的映射与应用的生命周期绑定,并且这份映射在整个应用当中有且仅有一份,且不可更改。

               4.让我们用于分派请求的过滤器,使用映射信息将客户请求对应到相应的servlet去处理,并且将分派逻辑移回过滤器,从而彻底删除简单工厂,即ServletFactory。

特别说一下,这四步当中,其中第三步是可选的,但也是必须的,因为如果不做这种处理,那么你就等着你的项目N长时间打开一个网页吧。

以上是简单工厂给我们的启示,具体如何实现这样一个基于注解的请求分配的架构,LZ不再给各位一一演示,因为这已经只剩下一个堆积代码的过程,具体的实现方案已经有了,如果各位读者有兴趣,可以私底下尝试一下这种方式。

好了,工厂方法模式就给各位分享到这吧,感谢各位的欣赏。

版权声明



作者:zuoxiaolong(左潇龙)

出处:博客园左潇龙的技术博客--http://www.cnblogs.com/zuoxiaolong

您的支持是对博主最大的鼓励,感谢您的认真阅读。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

时间: 2024-08-04 03:31:21

(四) 工厂方法模式的相关文章

Java设计模式菜鸟系列(四)工厂方法模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39760895 工厂方法模式(Factory Method) 工厂方法:顾名思义,就是调用工厂里的方法来生产对象(产品)的. 工厂方法实现方式有3种: 一.普通工厂模式.就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 1.uml建模图: 2.代码实现 /** * 示例(一):普通工厂方法 * * 缺点:如果传递的字符串出错,则不能正确创建对象 */ interface Sen

从王者荣耀看设计模式(十四.工厂方法模式)

从王者荣耀看设计模式(工厂方法模式) 二.简介 王者荣耀游戏设计师根据英雄技能.属性.天赋等因素,将英雄划分为射手.辅助.打野.法师.坦克.战士等职业.一局比赛存在多类英雄可供选择.玩家在挑选游戏英雄时,合理的英雄职业搭配是赢得游戏胜利的基本保证. 三.工厂方法模式 工厂方法模式(Factory Method Pattern):工厂方法模式又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式.在

Java 设计模式(四)-工厂方法模式 (FactoryMethod Pattern)

1     概念定义 1.1   定义 定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 1.2   类型 创建类模式 2     原理特征 2.1   类图 2.2   优点 1)封装性良好,代码结构清晰 2)可拓展性高,只需修改一下工厂方法或拓展一个工厂类 3)屏蔽产品类,调用者不需要关心产品类的变化 4)高度解耦,高层模块只需知道产品抽象类,无需关注实现 2.3   缺点 代码复杂度增加 3     .应用拓展 3.1   应用场景 1)    

设计模式之_简单工厂模式、工厂方法模式、抽象工厂模式 、策略模式、策略与工厂的区别(转)

一.前言 话说十年前,有一个爆发户,他家有三辆汽车(Benz(奔驰).Bmw(宝马).Audi(奥迪)),还雇了司机为他开车.不过,爆发户坐车时总是这样:上Benz车后跟司机说“开奔驰车!”,坐上Bmw后他说“开宝马车!”,坐上 Audi后他说“开奥迪车!”.你一定说:这人有病!直接说开车不就行了?!而当把这个爆发户的行为放到我们程序语言中来,我们发现C语言一直是通过这种方式来坐车的 幸运的是这种有病的现象在OO语言中可以避免了.下面以Java语言为基础来引入我们本文的主题:工厂模式! 二.简介

设计模式学习第四天:2.3工厂方法模式以及演化

一. 工厂方法(Factory Method)模式      工厂方法(FactoryMethod)模式是类的创建模式,其用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中.      工厂方法模式是简单工厂模式的进一步抽象和推广.由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点.      在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做.这个核心类仅仅负责给出具体工厂必须实现的接口,而不接触哪一个产品类被实例化这种细

Java设计模式从精通到入门四 简单工厂方法模式

简单工厂方法模式 属于23中设计模式中创建型类型. 核心思想:工厂提供创建对象的接口,由子类决定实例化哪一个子类. 来源 ? 设计模式之禅中的例子,女娲造人,通过八卦炉来进行造人,没有烧熟的为白人,烧太熟的为黑人,刚好的为黄种人的例子进行程序展示 女娲作为client,八卦炉为工厂类,人类为具体实现类,有皮肤的区别. UML类图 图一 Factory: 工厂类的接口,根据类类型,提供只能创建Human子类,或者子孙类的方法. HumanFactory: 工厂类的具体实现,通过反射获取该对象. H

设计模式(四)——抽象工厂方法模式

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题, 但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销. 此时,我们可以考虑将一些相关的产品组成一个"产品族",由同一个工厂来统一生产 原文地址:https://www.cnblogs.com/xiangtingshen/p/10356907.html

java语言实现创建型设计模式—工厂方法模式

一.描述 基于简单工厂模式中将所有类的创建和初始化放在一个工厂类中出现的问题,我们引进了工厂方法模式,该模式是GoF总结的23种设计模式的第一种,这个设计模式将一个工厂类拆分成多个具体的工厂类,每个具体的工厂类负责相应的类的对象的创建. 在工厂方法模式中,抽象工厂类负责定义创建对象的接口,具体对象的创建由实现该抽象工厂的具体工厂类来完成,它由四部分组成:抽象工厂类.实现抽象工厂类的具体工厂类.抽象类.实现抽象类的具体类. 二.工厂方法模式的优缺点 优点:在工厂方法模式中,创建对象的任务由具体的工

设计模式三:工厂方法模式

疑惑解答: 1.interface接口不能够实例化(new),但是可以定义引用来指向继承该接口的实体类: 如: interface animal{ void eat(); } class Cat implements animal{ public void eat(){ System.out.println("The cat eat!"); } } animal an = new Cat(); 一.什么是工厂方法模式 1.是简单工厂模式的进一步抽象: 2.定义一个创建产品对象的工厂接口