为了账号安全,请及时绑定邮箱和手机立即绑定

java设计模式--工厂模式

标签:
Java 设计

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

工厂方法模式中定义了一个工厂接口,而具体的创建工作推迟到具体的工厂类,它是对简单工厂模式中的工厂类进一步抽象化,从而产生一个工厂类的抽象和实现体系,从而弥补简单工厂模式对修改开放的诟病。
图中左半部分是产品抽象和实现体系,右半部分是工厂抽象和实现体系。每一个工厂负责创造一种产品,这就省去了简单工厂中的elseif判断,由客户端决定实例化一个特定的工厂去创建相应的产品。
具体的代码实现:
抽象产品接口:

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();
    }
}

现在可以在具体的工厂和产品之间切换,并且不需要修改任何代码,就可以让原来的程序正常运行,这也是工厂方法模式对扩展开放的表现,另外工厂方法模式弥补了简单工厂模式不满足开闭原则(拓展开放,修改关闭)的诟病,当我们需要增加产品时,只需要增加相应的产品和工厂类,而不需要修改现有的代码。

下面我们还是用数据库连接来说明工厂模式。
为了统一各个数据库操作的标准,于是有了JDBC的API,它用于给我们提供一系列统一的,标准化的操作数据库的接口。其实JDBC的各个类或接口,就是我们操作数据库的过程中各个协助者的抽象,这样的设计是为了让我们对数据库的操作依赖于抽象(用抽象构建框架,用细节扩展实现)。
JDBC API(即抽象的接口或类)就是整个数据库操作的框架,而各个数据库的驱动就是那些细节。而我们的操作依赖于JDBC API,而不是任何一个具体数据库的细节。其中最主要的就是两个接口。
第一个接口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();
}

上面只保留了这个类注释的第一句话,翻译过来是这是一个任何驱动类都必须实现的接口。也就是说每个数据库厂商都必须实现这个接口来提供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;

}

这里只留下了两个方法,这两个方法相信各位读者都非常熟悉,它们都是我们最经常用的方法之二。
以上两个接口作为JDBC API的一部分,它们相当于告诉了数据库生产厂商两个要求。
第一,数据库厂商要提供一个数据库驱动类,它的作用可以是可以创造数据库连接,而这个数据库连接向上转型为我们JDBC的Connection。
第二,数据库厂商要提供一个数据库连接的实现类,这个实现类可以执行具体数据库的各个操作,比如帮我们执行SQL,返回执行结果,关闭连接等等。

工厂方法模式就是提供一个抽象的工厂,一个抽象的产品,在上述当中相当于Driver(数据库连接工厂)和Connection(抽象产品),实现的一方需要提供一个具体的工厂类(比如mysql驱动Driver)和一个具体的产品(比如mysql数据库连接Connection)。
客户端调用时不依赖于具体工厂和产品(即到底是mysql驱动,mysql数据库连接还是oracle驱动,oracle连接,我们不需要管,我们只管使用抽象的driver和connection),而是依赖于抽象工厂和抽象产品完成工作。
数据库连接还有 一个DriverManager类,在这个设计当中扮演者一个管理者的角色,它帮我们管理数据库驱动,让我们不需要直接接触驱动接口,我们获取连接只需要和DriverManager打交道就可以,也就是说客户端依赖于DriverManager和Connection就可以完成工作,不再需要与Driver关联,所以上述说我们依赖于Driver和Connection,现在DriverManager帮我们管理Driver,那我们只需要依赖于DriverManager和Connection就可以了。

下面贴出mysql的driver的部分源码:

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,用户名和密码。
工厂方法模式的好处和适用的场景都相对比较好理解。
好处就是,从类关系上来说,它可以让客户端与具体的工厂与产品解耦,从业务角度来说,它让客户端与具体的产品解耦。
适用的场景就是我们需要一个产品帮我们完成一项任务,但是这个产品有可能有很多品牌(像这里的mysql,oracle),为了保持我们对产品操作的一致性,我们就可能要用到工厂方法模式。
工厂方法模式也有它所不足的地方。我们操纵数据库不再需要关心具体是哪个数据库,这样是很好很方便,那是因为这些产品的实现都不用自己写,都是数据库厂商写的。
假设产品数量巨多,而且需要我们亲手去逐个实现的时候,工厂方法模式就会增加系统的复杂性,到处都是工厂类和产品类,而且这里所说的工厂类和产品类只是概念上的,真正的产品可能不是一两个类就能搞定,否则mysql和oracle的驱动包不需要那么多类,而只是就一个Driver和一个Connection就足够了。
当然这也不是绝对,比如我们经常使用的HashSet和ArrayList,也是使用的工厂方法模式,各位看下他们的类图就看出来了。
图片描述
这便是工厂方法模式另外一种用法了,刚才因为我们不关心真正的产品是什么,所以我们直接使用抽象接口操作。但是我们使用iterable和iterator的时候,我们是关心真正产品的特性的,所以为了使用产品的特性,我们就需要使用产品特有的接口了,比如特殊的SortedSet可排序,比如ArrayList可以有重复元素,可以根据索引获取元素等等。当然你依然是可以使用iterable和iterator的,还是要看具体的场景。
两种使用方式一种是对使用者透明的,一种是不透明的,一种是使用者对具体的产品不关心,这种情况下,一般产品提供的功能是类似的。一种是使用者非常了解产品的特性,并想使用产品的特性,这种情况下,一般产品只提供最基本的一致的功能,但每个产品都会有自己独特的一面。

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
27
获赞与收藏
233

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消