设计模式-抽象工厂模式及应用

629 阅读6分钟

工厂方法模式 中,可以看到一种工厂,只 ==产生==一种类别的具体产品信息,同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

模式的定义与特点

抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

模式的结构与实现

抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。现在我们来分析其基本结构和实现方法。

  1. 模式的结构 抽象工厂模式的主要角色如下。
  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

抽象工厂模式的结构图: 在这里插入图片描述

代码案例:

  • 抽象工厂
public interface ElectronicsFactory {	
	/**
	 * 生产pad
	 * @return
	 */
	Pad producePad();
	
	/**
	 * 生产手机
	 * @return
	 */
	Mobile produceMobile();
	
}
  • 具体工厂1 (华为工厂)
public class HuaweiFactory implements ElectronicsFactory{

	@Override
	public Pad producePad() {
		return new HuaweiPad();
	}

	@Override
	public Mobile produceMobile() {
		return new HuaweiMobile();
	}

}
  • 具体工厂2 (苹果工厂)
public class AppleFactory implements ElectronicsFactory{

	@Override
	public Pad producePad() {
		return new Ipad();
	}

	@Override
	public Mobile produceMobile() {
		return new IphoneMoible();
	}

}
  • 抽象产品1 手机
public abstract class Mobile {
	public abstract void produce();

}
  • 抽象产品2 平板
public abstract class Pad {
	
	public abstract void produce();
}
  • 具体产品1-1 iphone手机
public class IphoneMoible extends Mobile{
	public void produce() {
		System.out.println("Apple produce Iphone12 mobile");
	}

}
  • 具体产品1-2 华为手机
public class HuaweiMobile extends Mobile {

	public void produce() {
		System.out.println("Huawei produce P40 mobile");		
	}
}
  • 具体产品2-1 苹果平板ipad
public class Ipad extends Pad{

	@Override
	public void produce() {
		System.out.println("Apple produce ipad air");
	}
}
  • 具体产品2-2 华为平板mate
public class HuaweiPad extends Pad{

	@Override
	public void produce() {
		System.out.println("Huawei produce mate pad");
		
	}
}
  • client 使用方
public class Client {

	public static void main(String[] args) {
		// 指定苹果电子工厂
        ElectronicsFactory electronicsFactory = new AppleFactory();
        //生产苹果系列产品
        Pad pad = electronicsFactory.producePad();
        Mobile mobile =  electronicsFactory.produceMobile();
        //生产
        pad.produce();
        mobile.produce();
	}

}

执行结果:

Apple produce ipad air
Apple produce Iphone12 mobile

案例类图 在这里插入图片描述

优缺点

优点: 抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。

  • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  • 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
  • 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

缺点: 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

使用场景

使用抽象工厂模式一般要满足以下条件。

  • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

抽象工厂模式通常适用于以下场景:

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

抽象工厂模式在JDK中源码分析

  • 抽象工厂在数据库连接及使用场景的运用 在我们使用原生的JDBC数据库连接时,会写如下代码:
public static void testJdbcSql(){
		String driverClassName = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://127.0.0.1:3306/test";
		String mysqlusername = "root";
		String password = "123";
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try
		{
			Class.forName(driverClassName);
			con = DriverManager.getConnection(url, mysqlusername, password);
			String sql = "SELECT * FROM USER WHERE username=?";
			...//略
		} catch (Exception e){
            ...//略
		}
	}

我们所有的操作都是获取完Connection连接对象进行后续操作的,java.sql.Connection是个接口,Connection 接口中有几个抽象方法,源码如下所示。

public interface Connection {
    //提供一个执行对象
    Statement createStatement() throws SQLException;
    //提供一个支持预编译的执行对象
    PreparedStatement prepareStatement(String sql) throws SQLException;
    //提供一个支持存储过程的执行对象
    CallableStatement prepareCall(String sql) throws SQLException;
}

当我们通过Class.forName(driverClassName),实则是往 DriverManager注册驱动,源码如下:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //注册DriverManager
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

public class DriverManager {
    // 注册的JDBC驱动程序列表
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    ....//省略
    // 注册驱动
    public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }
        println("registerDriver: " + driver);
    }
    .... //省略代码
}

驱动注册完成后,我们是可以获取通过DriverManager.getConnection(url, mysqlusername, password) 去获取相应的连接,源码如下:


public class DriverManager {
  
// 获取连接
private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
      ... //省略
 }

各个组件在抽象工厂中所扮演的角色,Connection 是一个经典的抽象工厂,而 StatementPreparedStatementCallableStatementConnection 这个抽象工厂中提供的三个抽象产品。

Driver 起到 Client 的作用,我们只需要把 Driver 注册进 DriverManager,就可以生成需要的 Connection。每次操作数据库只需要使用 Java 提供的这套接口就可以,不需要考虑使用的是什么 SQL 数据库(不考虑特殊SQL语法的情况下)。

这些抽象工厂与抽象产品均由对应的数据库驱动实现,下面以 MySQL 与 Oracle 的驱动进行举例。

UML结构图: 在这里插入图片描述

✨✨ 欢迎🔔订阅个人的微信公众号 享及时博文更新

✨✨ 个人GitHub地址