工厂模式(Factory Pattern)是一种创建对象的设计模式,属于 创建型模式(Creational Pattern)之一。其主要目的是通过定义一个工厂方法来创建对象,而不是直接通过 new
关键字实例化对象,从而将对象的创建过程与使用过程分离。这种方式能够减少代码中的耦合度,提高代码的可维护性、可扩展性,并符合面向对象设计的 单一职责原则和开放封闭原则。
工厂模式的核心思想是让子类决定实例化哪一个类,而父类提供创建对象的接口。它将对象的创建推迟到子类中执行。
1. 工厂模式的结构
工厂模式主要由以下几部分组成:
-
抽象产品(Product): 定义了产品的抽象接口,所有具体产品都会实现这个接口。
-
具体产品(ConcreteProduct): 实现了抽象产品接口的具体类,提供具体的实现。
-
抽象工厂(Creator): 定义了一个工厂方法,返回一个产品对象。此方法可以是抽象方法,也可以是具体方法,具体由子类实现。
-
具体工厂(ConcreteCreator): 实现了抽象工厂中的工厂方法,并返回具体的产品对象。
2. 工厂模式的分类
工厂模式可以根据工厂方法的数量和使用方式,分为以下几种类型:
-
简单工厂模式(Simple Factory): 又称静态工厂方法模式,它通过一个工厂类来创建对象。这个工厂类决定了具体产品的类型。简单工厂模式并不严格属于设计模式,因为它不符合开闭原则(即扩展时需要修改工厂类)。但它的实现比较简单,适用于产品类型固定的场景。
优点:
- 客户端只依赖工厂类提供的接口,而不需要了解对象的具体类。
- 代码可读性高,易于理解。
缺点:
- 违反了开闭原则,如果产品需要扩展,必须修改工厂类。
- 增加了工厂类的复杂度,尤其在产品种类较多时,工厂类会变得庞大。
示例代码:
// 抽象产品 public interface Product { void doSomething(); } // 具体产品A public class ProductA implements Product { @Override public void doSomething() { System.out.println("Product A is doing something."); } } // 具体产品B public class ProductB implements Product { @Override public void doSomething() { System.out.println("Product B is doing something."); } } // 工厂类 public class SimpleFactory { public static Product createProduct(String type) { if ("A".equals(type)) { return new ProductA(); } else if ("B".equals(type)) { return new ProductB(); } else { return null; } } } // 客户端代码 public class Client { public static void main(String[] args) { Product product = SimpleFactory.createProduct("A"); product.doSomething(); } }
-
工厂方法模式(Factory Method): 也叫做多态工厂方法,它通过定义一个抽象工厂类,允许子类去决定实例化哪一个具体产品。每个具体工厂类负责创建一个具体的产品。工厂方法模式使得扩展产品种类变得更加容易,符合 开闭原则。
优点:
- 每个工厂只负责创建一个具体的产品,符合单一职责原则。
- 增加新产品时,只需增加新的具体产品类和具体工厂类,而无需修改现有代码。
缺点:
- 需要为每个产品创建一个工厂类,增加了类的数量和复杂度。
示例代码:
// 抽象产品 public interface Product { void doSomething(); } // 具体产品A public class ProductA implements Product { @Override public void doSomething() { System.out.println("Product A is doing something."); } } // 具体产品B public class ProductB implements Product { @Override public void doSomething() { System.out.println("Product B is doing something."); } } // 抽象工厂 public abstract class Creator { public abstract Product factoryMethod(); } // 具体工厂A public class ConcreteCreatorA extends Creator { @Override public Product factoryMethod() { return new ProductA(); } } // 具体工厂B public class ConcreteCreatorB extends Creator { @Override public Product factoryMethod() { return new ProductB(); } } // 客户端代码 public class Client { public static void main(String[] args) { Creator creator = new ConcreteCreatorA(); Product product = creator.factoryMethod(); product.doSomething(); } }
-
抽象工厂模式(Abstract Factory): 抽象工厂模式在工厂方法模式的基础上进一步抽象,提供了一个接口,用于创建一系列相关或相互依赖的产品对象,而无需指定具体产品类。抽象工厂通常用于产品家族较大,且产品间有一定关联性的场景。
优点:
- 可以创建一系列相关的产品对象,避免了将这些产品对象散布在各个地方。
- 增加新的产品族时,不需要修改现有代码,符合开闭原则。
缺点:
- 代码结构复杂,尤其是在产品家族较多时,增加了工厂类的复杂度。
- 每新增一个产品族,就必须新增一个具体工厂类。
示例代码:
// 抽象产品A public interface ProductA { void doSomethingA(); } // 具体产品A1 public class ProductA1 implements ProductA { @Override public void doSomethingA() { System.out.println("Product A1 is doing something."); } } // 具体产品A2 public class ProductA2 implements ProductA { @Override public void doSomethingA() { System.out.println("Product A2 is doing something."); } } // 抽象产品B public interface ProductB { void doSomethingB(); } // 具体产品B1 public class ProductB1 implements ProductB { @Override public void doSomethingB() { System.out.println("Product B1 is doing something."); } } // 具体产品B2 public class ProductB2 implements ProductB { @Override public void doSomethingB() { System.out.println("Product B2 is doing something."); } } // 抽象工厂 public interface AbstractFactory { ProductA createProductA(); ProductB createProductB(); } // 具体工厂1 public class ConcreteFactory1 implements AbstractFactory { @Override public ProductA createProductA() { return new ProductA1(); } @Override public ProductB createProductB() { return new ProductB1(); } } // 具体工厂2 public class ConcreteFactory2 implements AbstractFactory { @Override public ProductA createProductA() { return new ProductA2(); } @Override public ProductB createProductB() { return new ProductB2(); } } // 客户端代码 public class Client { public static void main(String[] args) { AbstractFactory factory = new ConcreteFactory1(); ProductA productA = factory.createProductA(); productA.doSomethingA(); ProductB productB = factory.createProductB(); productB.doSomethingB(); } }
3. 工厂模式+配置文件
在实际的系统设计中,工厂模式与配置文件的结合使用,能够提高系统的灵活性与可扩展性,尤其是在需要根据外部配置动态创建不同对象的场景中。这种设计方式能够使得代码更加灵活、易于维护和扩展。
工厂模式 + 配置文件的实现方式
假设我们需要根据配置来决定使用哪种数据库连接。可以使用工厂模式来封装数据库连接的创建逻辑,并通过配置类来读取外部的数据库类型配置,从而动态创建不同的数据库连接对象。
示例代码:
- 定义产品接口(DatabaseConnection)
public interface DatabaseConnection {
void connect();
}
- 定义具体产品(MySQLConnection, PostgreSQLConnection)
public class MySQLConnection implements DatabaseConnection{
@Override
public void connect() {
System.out.println("正在连接MySQL...");
}
}
public class PostgreSQLConnection implements DatabaseConnection{
@Override
public void connect() {
System.out.println("PostqreSQL正在连接中...");
}
}
- config.properties文件
MySQL=com.study.factoryPattern.factoryAndConfigurationFile.MySQLConnection
PostgreSQL=com.study.factoryPattern.factoryAndConfigurationFile.PostgreSQLConnection
- 工厂类(DatabaseConnectionFactory)
public class Factory {
private static Map<String,DatabaseConnection> dataBaseConnections = new HashMap<>();
static {
//导入配置文件,并创建单例,储存在map中
Properties properties = new Properties();
InputStream resourceAsStream = Factory.class.getClassLoader().getResourceAsStream("config.properties");
try {
properties.load(resourceAsStream);
Set<Object> keys = properties.keySet();
for (Object key : keys) {
String className = properties.getProperty((String) key);
Class clazz = Class.forName(className);
DatabaseConnection instance = (DatabaseConnection) clazz.newInstance();
dataBaseConnections.put((String) key, instance);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static DatabaseConnection createConnection(String name){
return dataBaseConnections.get(name);
}
}
- 客户端代码
public class Client {
public static void main(String[] args) {
// DatabaseConnection mySQL = Factory.createConnection("MySQL");
DatabaseConnection mySQL = Factory.createConnection("PostgreSQL");
mySQL.connect();
}
}