一、抽象工厂模式概述
抽象工厂提供一个接口,通过该接口可以在不指定具体类的情况下,创建一系列相关的或相互独立的对象。
抽象工厂模式是工厂方法模式的泛化版,工厂方法模式是一种特殊的抽象工厂模式。在工厂方法模式中,每一个具体工厂只能生产一种具体产品,而在抽象工厂方法模式中,每一个具体工厂可以生产多个具体产品。在抽象工厂模式中,有两个重要的概念:
产品等级结构:产品等级结构是产品的继承结构,如一个抽象类Button,其子类有WindowsButton、LinuxButton、UnixButton,则抽象类Button和具体系统的Button类之间构成了一个产品等级结构。
产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如由Windows系统(工厂)生成的WindowsButton、WindowsText,WindowsButton位于Button等级结构中,而WindowsText位于Text产品等级结构中。
产品族与产品等级结构的典型关系图如下
在上图中,不同颜色的多个正方形、圆形和三角形分别构成了3个不同的产品等级结构,而相同颜色的正方形、圆形和三角形分别构成了5产品族。每一个对象都位于某个产品族,并属于某个产品等级结构。
当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。
在上图中,每一个具体工厂可以生产属于一个产品族的所有产品,生产颜色相同的正方形、圆形和三角形,所生产的产品又位于不同的产品等级结构中。如果使用工厂方法模式,要生产上图所有对象需要15个具体工厂,而使用抽象工厂模式只需要5个具体工厂即可,这大大减少了系统中类的个数。
抽象工厂模式的一般结构图如下
AbstractFactory(抽象工厂):抽象工厂用于声明生成抽象产品的方法,在一个抽象工厂中可以定义一组方法,每一个方法对应一个产品等级结构。
ConcreteFactory(具体工厂):具体工厂实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
AbstractProduct(抽象产品):抽象产品为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法。
Product(具体产品):具体产品定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。
二、抽象工厂模式实例
下面通过一个实例来加深对抽象工厂模式的认识。某系统为了改进数据库操作的性能,自定义了数据库连接对象Connection和语句对象Statement,可针对不同类型的数据库提供不同的连接对象和语句对象。而且用户可以通过配置文件等方式根据实际需要动态更换系统数据库存。使用抽象工厂模式设计该系统,设计类图如下
(1)抽象工厂类DBFactory(数据库工厂类),在该类中定义了抽象工厂方法:用于获取连接到数据库的对象的方法getConnection()和用于创建操作数据库的statements对象的方法getStatements(),即针对每一种产品族和产品都提供了一个对应的工厂方法。
public interface DBFactory {
public Connection getConnection();
public Statements getStatements();
}
(2)具体工厂类MySQLFactory,该类提供了获取连接到MySQL数据库的对象的方法实现,及获取操作MySQL数据库的statements对象的方法实现。
public class MySQLFactory implements DBFactory{
public Connection getConnection()
{
return new MySQLConnection();
}
public Statements getStatements()
{
return new MySQLStatements();
}
}
(3)具体工厂类OracleFactory,该类提供了获取连接到Oracle数据库的对象的方法实现,及获取操作Oracle数据库的statements对象的方法实现。
public class OracleFactory implements DBFactory{
public Connection getConnection()
{
return new OracleConnection();
}
public Statements getStatements()
{
return new OracleStatements();
}
}
(4)抽象产品接口Statements,该接口包含业务方法statements ()的声明,表示statements语句集,其实可以用类来代替该接口。
public interface Statements {
public void statements();
}
(5)具体产品类MySQLStatements,该类实现了业务方法statements,实际开发中应该表示定义了用于操作MySQL数据库的statements语句。
public class MySQLStatements implements Statements{
public void statements()
{
System.out.println("Execute MySQL's statements.");
}
}
(6)具体产品类OracleStatements,该类实现了业务方法statements,实际开发中应该表示定义了用于操作Oracle数据库的statements语句。
public class OracleStatements implements Statements{
public void statements()
{
System.out.println("Execute Oracle's statements.");
}
}
(7)抽象产品接口Connection,该接口包含业务方法connection()的声明,在实际开发中表示连接到数据库,其实可以用类来代替该接口。
public interface Connection {
public void connection();
}
(8)具体产品类MySQLConnection,该类中实现了业务方法connection(),在实际开发中表示连接到MySQL数据库。
public class MySQLConnection implements Connection{
public void connection()
{
System.out.println("Connect to the MySQL database.");
}
}
(9)具体产品类OracleConnection,该类中实现了业务方法connection(),在实际开发中表示连接到Oracle数据库。
public class OracleConnection implements Connection{
public void connection()
{
System.out.println("Connect to the Oracle database.");
}
}
(10)测试类 Clients
public class Clients {
public static void main(String args[])
{
try{
DBFactory factory;
Connection con;
Statements st;
//创建哪种类型的工厂,可以改为由程序从配置文件中读取得到
factory = new OracleFactory();
con = factory.getConnection();
con.connection();
st = factory.getStatements();
st.statements();
}catch(Exception e)
{
System.out.println(e.getMessage());
}
}
}
三、抽象工厂模式总结
1. 抽象工厂模式的起源
抽象工厂模式最初是用来创建在不同操作系统的图形环境下都能够运行的系统。从下图中可以看出,图中有两个产品等级结构,分别是Buttont和Text;有三个产品族:UNIX产品族、Linux产品族和Windows产品族。Windows的Button和Text构成了一个Windows产品族,而不同操作系统下的Button构成了一个产品等级结构。在实际情况下,一个系统在某一时刻只能够在某一操作系统的图形环境下运行。所以系统实际上只能消费同一个产品族中的产品 。通过抽象工厂模式,系统可以在运行时动态判断操作系统的类型,选择对应的具体工厂来创建图形构件,从而使得系统可以兼容不同的操作系统,在不同的操作系统中呈现与该系统一致的外观。
2. 抽象工厂模式的优缺点
(1)优点:抽工厂模式隔离了具体类的生成,使用客户并不需要知道什么被创建,也使用得更换一个具体工厂变得相对容易。应用抽象工厂模式可以实现高内聚低耦合的软件设计,而且它能保证客户端始终只使用同一个产品族中的对象。
(2)缺点:在添加新的产品对象时,难以扩展抽象工厂来生产新的产品,因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就得对该接口进行扩展,而这也导致了对抽象工厂角色及其所有子类的修改,显然这样很麻烦。