在软件开发中,对象的创建是一个基本操作。但直接在代码中使用 new 关键字创建对象会导致代码耦合度高、可维护性差。工厂模式(Factory Pattern)作为一种创建型设计模式,能有效解决这些问题。本文将深入探讨 Java 中的工厂模式,通过实例展示其实现方式、优势及适用场景。

一、工厂模式的定义与概念

工厂模式的核心思想是:将对象的创建和使用分离,把对象创建逻辑封装在一个工厂类中,从而提高代码的可维护性和可扩展性。

工厂模式主要分为三种类型:

  • 第一种:简单工厂模式(Simple Factory):不属于23种设计模式之一。简单工厂模式又叫做:静态 工厂方法模式。简单工厂模式是工厂方法模式的一种特殊实现。
  • 第二种:工厂方法模式(Factory Method):是23种设计模式之一。
  • 第三种:抽象工厂模式(Abstract Factory):是23种设计模式之一。

二、简单工厂模式

简单工厂模式是工厂模式的基础版本,它定义了一个工厂类来创建产品对象。

  • 抽象产品 角色

  • 具体产品 角色

  • 工厂类 角色

示例1:兵工厂

抽象产品 角色:Weapon

public abstract class Weapon {
 
    /**
     * 所有的武器都可以攻击。
     */
    public abstract void attack();
 
}

具体产品 角色:Dagger、Fighter、Tank

public class Dagger extends Weapon{
    @Override
    public void attack() {
        System.out.println("砍丫的!!!");
    }
}
public class Fighter extends Weapon{
    @Override
    public void attack() {
        System.out.println("战斗机抛下小男孩!!!!");
    }
}
public class Tank extends Weapon{
    @Override
    public void attack() {
        System.out.println("坦克开炮!!!");
    }
}

工厂类 角色

public class WeaponFactory {
 
    /**
     * 静态方法。要获取什么产品?就看你传什么参数,传TANK获取坦克,传DAGGER获取匕首,传FIGHTER获取战斗机
     * 简单工厂模式中有一个静态方法,所以被称为:静态工厂方法模式。
     * @param weaponType
     * @return
     */
    public static Weapon get(String weaponType){
        if ("TANK".equals(weaponType)) {
            return new Tank();
        } else if ("DAGGER".equals(weaponType)) {
            return new Dagger();
        } else if ("FIGHTER".equals(weaponType)) {
            return new Fighter();
        } else {
            throw new RuntimeException("不支持该武器的生产");
        }
    }
 
}

测试:

public class Test {
    public static void main(String[] args) {
 
        // 需要坦克
        // 对于我客户端来说,坦克的生产细节,我不需要关心,我只需要向工厂索要即可。
        // 简单工厂模式达到了什么呢?职责分离。客户端不需要关心产品的生产细节。
        // 客户端只负责消费。工厂类负责生产。一个负责生产,一个负责消费。生产者和消费者分离了。这就是简单工厂模式的作用。
        Weapon tank = WeaponFactory.get("TANK");
        tank.attack();
        // 需要匕首
        Weapon dagger = WeaponFactory.get("DAGGER");
        dagger.attack();
        // 需要战斗机
        Weapon fighter = WeaponFactory.get("FIGHTER");
        fighter.attack();
    }
}

简单工厂模式的缺点?    

缺点一:假设现在需要扩展一个新的产品,WeaponFactory工厂类的代码是需要修改的,显然违背了OCP原则。  

缺点二:工厂类的责任比较重大,不能出现任何问题,因为这个工厂类负责所有产品的生产,称为全能类,或者有人把它叫做上帝类。      

这个工厂类一旦出问题,整个系统必然全部瘫痪。(不要把所有鸡蛋放到一个篮子里面哦。) 

示例2:交通工具工厂示例

假设我们正在开发一个模拟城市交通系统的游戏,需要创建不同类型的交通工具(汽车、自行车、公交车)。使用简单工厂模式可以这样设计:

 定义产品接口

// 交通工具接口
public interface Vehicle {
    void drive();
    int getMaxSpeed();
}

 实现具体产品类

// 汽车
public class Car implements Vehicle {
    @Override
    public void drive() {
        System.out.println("驾驶汽车在公路上行驶");
    }

    @Override
    public int getMaxSpeed() {
        return 180;
    }
}

// 自行车
public class Bicycle implements Vehicle {
    @Override
    public void drive() {
        System.out.println("骑自行车在自行车道上行驶");
    }

    @Override
    public int getMaxSpeed() {
        return 25;
    }
}

// 公交车
public class Bus implements Vehicle {
    @Override
    public void drive() {
        System.out.println("驾驶公交车在城市线路上行驶");
    }

    @Override
    public int getMaxSpeed() {
        return 80;
    }
}

创建简单工厂类

// 交通工具工厂
public class VehicleFactory {
    public static Vehicle createVehicle(String vehicleType) {
        if (vehicleType == null) {
            return null;
        }
        return switch (vehicleType.toLowerCase()) {
            case "car"    -> new Car();
            case "bicycle" -> new Bicycle();
            case "bus"    -> new Bus();
            default       -> throw new IllegalArgumentException("未知的交通工具类型: " + vehicleType);
        };
    }
}

 客户端使用

public class Client {
    public static void main(String[] args) {
        // 创建汽车
        Vehicle car = VehicleFactory.createVehicle("car");
        car.drive();
        System.out.println("最高速度: " + car.getMaxSpeed() + " km/h");

        // 创建自行车
        Vehicle bicycle = VehicleFactory.createVehicle("bicycle");
        bicycle.drive();
        System.out.println("最高速度: " + bicycle.getMaxSpeed() + " km/h");

        // 创建公交车
        Vehicle bus = VehicleFactory.createVehicle("bus");
        bus.drive();
        System.out.println("最高速度: " + bus.getMaxSpeed() + " km/h");
    }
}

简单工厂模式的优缺点:

  • 优点:实现简单,将对象创建和使用分离。
  • 缺点:工厂类职责过重,新增产品需修改工厂类,违反开闭原则。

三、工厂方法模式

工厂方法模式将对象创建延迟到子类,每个具体工厂类负责创建特定类型的产品。

工厂方法模式既保留了简单工厂模式的优点,同时又解决了简单工厂模式的缺点。 工厂方法模式的角色包括:

  • 抽象工厂角色

  • 具体工厂角色

  • 抽象产品角色

  • 具体产品角色

示例1:兵工厂

就是把工厂开成专门的坦克制造厂和飞机制造厂,各种厂抽象出一个接口厂

抽象产品角色 Weapon

abstract public class Weapon {
 
    public abstract void attack();
}

具体产品角色 Dagger Gun

public class Dagger extends Weapon{
 
    @Override
    public void attack() {
        System.out.println("砍丫的!!!");
    }
}
public class Gun extends Weapon{
    @Override
    public void attack() {
        System.out.println("开枪射击!!!");
    }
}

抽象工厂角色 WeaponFactory

abstract public class WeaponFactory {
 
    /**
     * 这个方法不是静态的。是实例方法。
     * @return
     */
    public abstract Weapon get();
 
}

具体工厂角色 DaggerFactory GunFactory

public class GunFactory extends WeaponFactory{
    @Override
    public Weapon get() {
        return new Gun();
    }
}
public class DaggerFactory extends WeaponFactory{
    @Override
    public Weapon get() {
        return new Dagger();
    }
}

Test

public class Test {
    public static void main(String[] args) {
        WeaponFactory weaponFactory = new DaggerFactory();
        Weapon dagger = weaponFactory.get();
        dagger.attack();
 
        WeaponFactory weaponFactory1 = new GunFactory();
        Weapon gun = weaponFactory1.get();
        gun.attack();
    }
}

如果想扩展一个新的产品,只要新增一个产品类,再新增一个该产品对应的工厂即可,例如新增:战斗机

public class Fighter extends Weapon{
    @Override
    public void attack() {
        System.out.println("战斗机发射核弹!");
    }
}
public class FighterFactory extends WeaponFactory{
    @Override
    public Weapon get() {
        return new Fighter();
    }
}

客户端程序:

public class Client {
    public static void main(String[] args) {
        WeaponFactory factory = new GunFactory();
        Weapon weapon = factory.get();
        weapon.attack();
 
        WeaponFactory factory1 = new FighterFactory();
        Weapon weapon1 = factory1.get();
        weapon1.attack();
 
        WeaponFactory factory2 = new DaggerFactory();
        Weapon weapon2 = factory2.get();
        weapon2.attack();
    }
}

执行结果如下:

我们可以看到在进行功能扩展的时候,不需要修改之前的源代码,显然工厂方法模式符合OCP原则。

工厂方法模式的优点:

一个调用者想创建一个对象,只要知道其名称就可以了。

扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

屏蔽产品的具体实现,调用者只关心产品的接口。

工厂方法模式的缺点:

每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

示例2:日志记录器工厂

假设我们需要创建不同类型的日志记录器(文件日志、数据库日志),使用工厂方法模式可以这样设计:

步骤 1:定义产品接口

// 日志记录器接口
public interface Logger {
    void log(String message);
}

步骤 2:实现具体产品类

// 文件日志记录器
public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("文件日志: " + message);
    }
}

// 数据库日志记录器
public class DatabaseLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("数据库日志: " + message);
    }
}

步骤 3:定义工厂接口

// 日志记录器工厂接口
public interface LoggerFactory {
    Logger createLogger();
}

步骤 4:实现具体工厂类

// 文件日志工厂
public class FileLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new FileLogger();
    }
}

// 数据库日志工厂
public class DatabaseLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new DatabaseLogger();
    }
}

步骤 5:客户端使用

public class Client {
    public static void main(String[] args) {
        // 创建文件日志工厂
        LoggerFactory fileLoggerFactory = new FileLoggerFactory();
        Logger fileLogger = fileLoggerFactory.createLogger();
        fileLogger.log("这是文件日志");

        // 创建数据库日志工厂
        LoggerFactory databaseLoggerFactory = new DatabaseLoggerFactory();
        Logger databaseLogger = databaseLoggerFactory.createLogger();
        databaseLogger.log("这是数据库日志");
    }
}

输出结果:

文件日志: 这是文件日志
数据库日志: 这是数据库日志

四、抽象工厂模式

示例1:兵工厂

抽象工厂模式相对于工厂方法模式来说,就是工厂方法模式是针对一个产品系列的,而抽象工厂模式是针对多个产品系列的,即工厂方法模式是一个产品系列一个工厂类,而抽象工厂模式是多个产品系列一个工厂类。

抽象工厂中包含4个角色:

  • 抽象工厂角色
  • 具体工厂角色
  • 抽象产品角色
  • 具体产品角色

抽象工厂模式的类图如下:

抽象工厂模式代码如下:

第一部分:武器产品族

public abstract class Weapon {
    public abstract void attack();
}
public class Gun extends Weapon{
    @Override
    public void attack() {
        System.out.println("开枪射击!");
    }
}
public class Dagger extends Weapon{
    @Override
    public void attack() {
        System.out.println("砍丫的!");
    }
}

第二部分:水果产品族

public abstract class Fruit {
    /**
     * 所有果实都有一个成熟周期。
     */
    public abstract void ripeCycle();
}
public class Orange extends Fruit{
    @Override
    public void ripeCycle() {
        System.out.println("橘子的成熟周期是10个月");
    }
}
public class Apple extends Fruit{
    @Override
    public void ripeCycle() {
        System.out.println("苹果的成熟周期是8个月");
    }
}

第四部分:具体工厂类

public class WeaponFactory extends AbstractFactory{
 
    public Weapon getWeapon(String type){
        if (type == null || type.trim().length() == 0) {
            return null;
        }
        if ("Gun".equals(type)) {
            return new Gun();
        } else if ("Dagger".equals(type)) {
            return new Dagger();
        } else {
            throw new RuntimeException("无法生产该武器");
        }
    }
 
    @Override
    public Fruit getFruit(String type) {
        return null;
    }
}
public class FruitFactory extends AbstractFactory{
    @Override
    public Weapon getWeapon(String type) {
        return null;
    }
 
    public Fruit getFruit(String type){
        if (type == null || type.trim().length() == 0) {
            return null;
        }
        if ("Orange".equals(type)) {
            return new Orange();
        } else if ("Apple".equals(type)) {
            return new Apple();
        } else {
            throw new RuntimeException("我家果园不产这种水果");
        }
    }
}

第五部分:客户端程序

public class Client {
    public static void main(String[] args) {
        // 客户端调用方法时只面向AbstractFactory调用方法。
        AbstractFactory factory = new WeaponFactory(); // 注意:这里的new WeaponFactory()可以采用 简单工厂模式 进行隐藏。
        Weapon gun = factory.getWeapon("Gun");
        Weapon dagger = factory.getWeapon("Dagger");
 
        gun.attack();
        dagger.attack();
 
        AbstractFactory factory1 = new FruitFactory(); // 注意:这里的new FruitFactory()可以采用 简单工厂模式 进行隐藏。
        Fruit orange = factory1.getFruit("Orange");
        Fruit apple = factory1.getFruit("Apple");
 
        orange.ripeCycle();
        apple.ripeCycle();
    }
}

执行结果:

抽象工厂模式的优缺点:

  • 优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

  • 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在AbstractFactory里加代码,又要在具体的里面加代码。

示例2:UI 组件工厂

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

假设我们需要创建不同风格的 UI 组件(按钮、文本框),使用抽象工厂模式可以这样设计:

步骤 1:定义产品接口

// 按钮接口
public interface Button {
    void paint();
}

// 文本框接口
public interface TextField {
    void display();
}

步骤 2:实现具体产品类

// Windows 风格按钮
public class WindowsButton implements Button {
    @Override
    public void paint() {
        System.out.println("绘制 Windows 风格按钮");
    }
}

// macOS 风格按钮
public class MacOSButton implements Button {
    @Override
    public void paint() {
        System.out.println("绘制 macOS 风格按钮");
    }
}

// Windows 风格文本框
public class WindowsTextField implements TextField {
    @Override
    public void display() {
        System.out.println("显示 Windows 风格文本框");
    }
}

// macOS 风格文本框
public class MacOSTextField implements TextField {
    @Override
    public void display() {
        System.out.println("显示 macOS 风格文本框");
    }
}

步骤 3:定义抽象工厂接口

// UI 组件工厂接口
public interface GUIFactory {
    Button createButton();
    TextField createTextField();
}

步骤 4:实现具体工厂类

// Windows 风格 UI 工厂
public class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }

    @Override
    public TextField createTextField() {
        return new WindowsTextField();
    }
}

// macOS 风格 UI 工厂
public class MacOSFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacOSButton();
    }

    @Override
    public TextField createTextField() {
        return new MacOSTextField();
    }
}

步骤 5:客户端使用

public class Client {
    public static void main(String[] args) {
        // 创建 Windows 风格 UI 工厂
        GUIFactory windowsFactory = new WindowsFactory();
        Button windowsButton = windowsFactory.createButton();
        TextField windowsTextField = windowsFactory.createTextField();
        windowsButton.paint();
        windowsTextField.display();

        // 创建 macOS 风格 UI 工厂
        GUIFactory macosFactory = new MacOSFactory();
        Button macosButton = macosFactory.createButton();
        TextField macosTextField = macosFactory.createTextField();
        macosButton.paint();
        macosTextField.display();
    }
}

输出结果:

绘制 Windows 风格按钮
显示 Windows 风格文本框
绘制 macOS 风格按钮
显示 macOS 风格文本框

抽象工厂模式的优缺点:

  • 优点:将具体产品创建封装,客户端无需依赖具体产品类。
  • 缺点:扩展产品族困难,新增产品需修改抽象工厂接口及所有子类。

五、三种工厂模式的对比

模式工厂类 / 接口产品创建逻辑扩展性
简单工厂具体类(静态方法)集中在工厂类中新增产品需修改工厂类,违反开闭原则
工厂方法抽象接口 + 具体工厂类由具体工厂类实现新增产品只需添加新的具体工厂类
抽象工厂抽象接口 + 具体工厂类由具体工厂类实现多个相关产品新增产品族容易,新增单个产品困难

六、Java 中工厂模式的实际应用

示例:数据库连接工厂

// 数据库连接接口
public interface DBConnection {
    void connect();
}

// MySQL 连接
public class MySQLConnection implements DBConnection {
    @Override
    public void connect() {
        System.out.println("连接到 MySQL 数据库");
    }
}

// Oracle 连接
public class OracleConnection implements DBConnection {
    @Override
    public void connect() {
        System.out.println("连接到 Oracle 数据库");
    }
}

// 数据库连接工厂
public class DBConnectionFactory {
    public static DBConnection getConnection(String dbType) {
        if (dbType == null) {
            return null;
        }
        if (dbType.equalsIgnoreCase("MYSQL")) {
            return new MySQLConnection();
        } else if (dbType.equalsIgnoreCase("ORACLE")) {
            return new OracleConnection();
        }
        throw new IllegalArgumentException("不支持的数据库类型: " + dbType);
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        DBConnection mysqlConn = DBConnectionFactory.getConnection("MYSQL");
        mysqlConn.connect();

        DBConnection oracleConn = DBConnectionFactory.getConnection("ORACLE");
        oracleConn.connect();
    }
}

七、总结

工厂模式是一种非常实用的创建型设计模式,它将对象的创建和使用分离,提高了代码的可维护性和可扩展性。在实际开发中,可以根据需求选择合适的工厂模式:

  • 简单工厂:适用于创建对象逻辑简单且产品种类较少的场景。
  • 工厂方法:适用于需要扩展性,经常新增产品的场景。
  • 抽象工厂:适用于创建一系列相关产品的场景。

通过合理应用工厂模式,可以构建更加灵活、可维护的软件系统。

更多推荐