设计模式(五)--工厂模式汇总
LZ想把简单工厂模式、工厂方法模式和抽象工厂模式整理到一篇博文当中,由浅入深,应该能方便理解和记忆,话不多说,进入正题。
一、简单工厂模式
定义:从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
总结成一句话就是,由一个工厂对象决定创建出哪一种产品类的实例。下面是百度百科中简单工厂模式的类图。
可以看出,上面总共有三种类,一个是工厂类Creator,一个是产品接口IProduct,一个是具体的产品类,例如产品A和产品B,这之中,工厂类负责整个创建产品的逻辑判断,所以为了使工厂类能够知道我们需要哪一种产品,我们需要在创建产品时传递一个参数给工厂类,去表明我们想要创建哪种产品。下面我们将类图翻译成java代码。
首先是产品接口。
public interface IProduct { public void method(); }
接下来是具体的产品类。
public class ProductA implements IProduct{ public void method() { System.out.println("产品A方法"); } }
public class ProductB implements IProduct{ public void method() { System.out.println("产品B方法"); } }
最后是工厂类。
public class Creator { private Creator(){} public static IProduct createProduct(String productName){ if (productName == null) { return null; } if (productName.equals("A")) { return new ProductA(); }else if (productName.equals("B")) { return new ProductB(); }else { return null; } } }
我们测试一下。
public class Client { public static void main(String[] args) { IProduct product1 = Creator.createProduct("A"); product1.method(); IProduct product2 = Creator.createProduct("B"); product2.method(); } }
测试结果。
以上就是简单工厂模式的样子,这个模式有个缺点,如果新加一种产品类,则Creator类的代码要相应改动,而工厂方法模式就很好的遵守了开闭原则,即对修改关闭,对扩展开放。
二、工厂方法模式
定义:工厂方法(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(); } }
测试结果。
可以看到,如果新增产品,只需要添加一个产品类,一个该产品的工厂类即可,不需要修改任何代码。LZ在看这个模式的时候看到别人给出的例子是JDBC API的设计,觉得非常贴切。LZ也把这个例子记录在这里。
众所周知,为了统一各个数据库操作的标准,于是有了JDBC的API,它提供了一系列统一的,标准化的操作数据库的接口,我们平时操作数据库,依赖的就是这些抽象,而不是具体的数据库的实现,那sun公司是怎么做到的呢?用的就是工厂设计模式。
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; }
connect方法即创造一个数据库连接,也就是说,Driver对象就是工厂模式中的Creator接口,即工厂类的抽象。
这个类除了connect方法以外,还有很多其他方法,篇幅原因,就不一一展开了,我们只关心核心方法,接口上有一句注释,翻译过来是这是一个任何驱动类都必须实现的接口。也就是说,sun公司明确规定,所有数据库厂商都必须实现这个接口来提供JDBC服务,即java数据库连接服务。
第二个接口是connect方法的返回抽象Connection对象,我们看一下源码,仍然只关心核心方法就可以。
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,返回执行结果,关闭连接等等。
LZ把类图画了一下,UML类图对设计模式这块非常重要,我个人的经验是,永远不要记代码,要记设计思想,记UML类图,记应用场景,所谓用抽象构建框架,用细节扩展实现。
多标准的工厂方法设计模式啊,sun公司正是用这个模式统一了数据库世界。工厂方法模式就是提供一个抽象的工厂,一个抽象的产品,在上述当中相当于Driver(数据库连接工厂)和Connection(抽象产品),实现的一方需要提供一个具体的工厂类(比如mysql驱动)和一个具体的产品(比如mysql数据库连接)。
客户端调用时不依赖于具体工厂和产品(即到底是mysql驱动,mysql数据库连接还是oracle驱动,oracle连接,我们程序猿不需要管的,我们只管使用抽象的driver和connection,对吧?),而是依赖于抽象工厂和抽象产品完成工作。
类图里还有个DriverMananger,DriverMananger在这个设计当中扮演者一个管理者的角色,它帮我们管理数据库驱动,让我们不需要直接接触驱动接口,我们获取连接只需要和DriverManager打交道就可以,也就是说客户端依赖于DriverManager和Connection就可以完成工作,不再需要与Driver关联,所以上述说我们依赖于Driver和Connection,现在DriverManager帮我们管理Driver,那我们只需要依赖于DriverManager和Connection就可以了。回想我们刚开始学习JDBC的时候,是不是只要让数据库厂商提供的具体数据库连接类加载,就可以直接从DriverManager里取连接了,所以这是sun公司为了方便编码给我们提供的一个管理类。
三、抽象工厂模式
抽象工厂模式算是工厂相关模式的终极形,基于上面的理解,我们不难理解抽象工厂模式,它与工厂方法唯一的区别就是工厂的接口里是一系列创造抽象产品的方法,而不再是一个,而相应的,抽象产品也不再是一个了,而是一系列相关的产品。这其实是工厂方法模式的一种扩展不是吗?
定义:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
我们看下百度百科给出的类图。
我们把类图翻译成代码看一下。首先是产品族,也就是类图右边部分。
package net; interface ProductA { void methodA(); } interface ProductB { void methodB(); } class ProductA1 implements ProductA{ public void methodA() { System.out.println("产品A系列中1型号产品的方法"); } } class ProductA2 implements ProductA{ public void methodA() { System.out.println("产品A系列中2型号产品的方法"); } } class ProductB1 implements ProductB{ public void methodB() { System.out.println("产品B系列中1型号产品的方法"); } } class ProductB2 implements ProductB{ public void methodB() { System.out.println("产品B系列中2型号产品的方法"); } }
左半部分。
package net; public interface Creator { ProductA createProductA(); ProductB createProductB(); }
package net; public class ConcreteCreator1 implements Creator{ public ProductA createProductA() { return new ProductA1(); } public ProductB createProductB() { return new ProductB1(); } } package net; public class ConcreteCreator2 implements Creator{ public ProductA createProductA() { return new ProductA2(); } public ProductB createProductB() { return new ProductB2(); } }
测试一下。
package net; public class Client { public static void main(String[] args) throws Exception { Creator creator = new ConcreteCreator1(); ProductA productA = creator.createProductA(); ProductB productB = creator.createProductB(); productA.methodA(); productB.methodB(); creator = new ConcreteCreator2(); productA = creator.createProductA(); productB = creator.createProductB(); productA.methodA(); productB.methodB(); } }
综上所述,简单工厂→工厂方法→抽象工厂,是一步步进化的过程。
1,首先从简单工厂进化到工厂方法,是因为工厂方法弥补了简单工厂对修改开放的弊端,即简单工厂违背了开闭原则。
2,从工厂方法进化到抽象工厂,是因为抽象工厂弥补了工厂方法只能创造一个的产品的弊端。
工厂设计模式可能对像LZ这样平时只针对业务编码的程序猿来说用到的机会少一点,但是我们在看源码的过程中一定会看到这个模式,比如前面提到的JDBC,现在相信再回头看JDBC的源码,就能看懂当年sun公司为什么要这么去设计代码,大牛们牛X的地方,我们才能真的体会到。