Java设计模式之工厂方法模式

概念

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口(工厂接口),但由子类决定要实例化的类(具体产品类)。该模式将对象的创建逻辑封装在子类中,使得客户端代码可以独立于对象的创建过程。

举例

假设我们有一个日志记录系统,需要支持不同类型的日志记录方式,比如文件日志、数据库日志、控制台日志等。我们可以使用工厂方法模式来设计这个系统。

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

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

public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        // 将日志输出到控制台
        System.out.println("ConsoleLogger: " + message);
    }
}
public interface LoggerFactory {
    Logger createLogger();
}
public class FileLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new FileLogger();
    }
}

public class DatabaseLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new DatabaseLogger();
    }
}

public class ConsoleLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new ConsoleLogger();
    }
}
public class Client {
    public static void main(String[] args) {
        // 使用文件日志工厂
        LoggerFactory fileLoggerFactory = new FileLoggerFactory();
        Logger fileLogger = fileLoggerFactory.createLogger();
        fileLogger.log("This is a file log message.");

        // 使用数据库日志工厂
        LoggerFactory databaseLoggerFactory = new DatabaseLoggerFactory();
        Logger databaseLogger = databaseLoggerFactory.createLogger();
        databaseLogger.log("This is a database log message.");

        // 使用控制台日志工厂
        LoggerFactory consoleLoggerFactory = new ConsoleLoggerFactory();
        Logger consoleLogger = consoleLoggerFactory.createLogger();
        consoleLogger.log("This is a console log message.");
    }
}

作用

1.解耦创建和使用

将对象的创建和使用分离,客户端代码不需要知道对象的创建细节,只需要通过工厂接口获取对象。

2.灵活扩展

新增日志记录方式时,只需添加新的日志记录器类和对应的工厂类,无需修改现有代码,符合开闭原则。

3.封装创建逻辑

将对象的创建逻辑封装在工厂类中,便于统一管理和维护,减少重复代码。

4.支持多态

通过工厂接口可以创建不同类型的对象,客户端代码可以基于多态进行操作,提高代码的可复用性和可维护性。

反例

不使用工厂方法模式

假设我们有一个日志记录系统,需要支持不同类型的日志记录方式,比如文件日志、数据库日志、控制台日志等。如果不使用工厂方法模式,可能会出现以下问题:

public class Logger {
    public static void log(String type, String message) {
        // 后续如有增加类型,这里的代码需修改才能扩展,违反开闭原则,耦合度也高
        if ("file".equals(type)) {
            // 文件日志记录逻辑
            System.out.println("FileLogger: " + message);
        } else if ("database".equals(type)) {
            // 数据库日志记录逻辑
            System.out.println("DatabaseLogger: " + message);
        } else if ("console".equals(type)) {
            // 控制台日志记录逻辑
            System.out.println("ConsoleLogger: " + message);
        } else {
            throw new IllegalArgumentException("Invalid logger type: " + type);
        }
    }
}

public class Client {
    public static void main(String[] args) {
        Logger.log("file", "This is a file log message.");
        Logger.log("database", "This is a database log message.");
        Logger.log("console", "This is a console log message.");
    }
}

存在的短板

1.违反单一职责原则

Logger类既负责日志记录逻辑,又负责根据类型创建不同的日志记录器,职责不单一。当需要新增日志记录类型时,需要修改Logger类的代码,违反开闭原则。

2.扩展性差

新增日志记录类型时,需要修改Logger类的log方法,添加新的if-else或switch-case分支。这会导致Logger类变得越来越臃肿,难以维护。

3.客户端代码与日志记录器耦合度高

客户端代码需要知道所有日志记录器的类型,并在调用时传入正确的类型字符串。如果类型字符串拼写错误,可能会导致运行时错误。

4.难以统一管理日志记录器的创建

日志记录器的创建逻辑分散在客户端代码中,难以统一管理和维护。例如,如果需要对日志记录器的创建进行额外的配置或初始化操作,需要在多个地方修改代码。

5.不利于多态的使用

这种实现方式下,客户端代码无法基于多态来操作日志记录器,只能通过类型字符串来区分不同的日志记录方式,代码的灵活性和可复用性较低。

相比之下,使用工厂方法模式可以将日志记录器的创建逻辑封装在工厂类中,客户端代码通过工厂接口获取日志记录器对象,解耦了客户端代码与日志记录器的创建过程,提高了代码的可扩展性、可维护性和灵活性。

什么是开闭原则

开闭原则(Open-Closed Principle, OCP)是软件设计中的一项核心原则,由伯特兰·梅耶(Bertrand Meyer)提出。该原则指出:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

核心思想

对扩展开放:软件实体应该能够通过扩展其功能来适应新的需求或变化,而无需修改其现有的代码。

对修改关闭:现有的代码在完成其功能后,应尽量避免被修改,以降低引入错误的风险并保持代码的稳定性。

举例说明

class Client {
    public static void main(String[] args) {
        // 直接依赖具体类
        Car car;
        String type = "sedan";
        if ("sedan".equals(type)) {
            // 实例化轿车
            car = new Sedan();
        } else if ("suv".equals(type)) {
            // 实例化SUV
            car = new SUV();
        }
        // 增加其它汽车类型需要在这里继续用else if,耦合性高
        car.drive();
    }
}

短板

1.违反开闭原则

新增车型(如Truck)时,必须修改客户端代码中的条件判断逻辑。

2.高耦合

客户端直接依赖具体类,难以替换或扩展产品。

3.重复代码

若多个地方需要创建对象,相同的条件判断逻辑会重复出现。

// 抽象产品
interface Car {
    void drive();
}

// 具体产品
class Sedan implements Car {
    @Override
    public void drive() { System.out.println("驾驶轿车"); }
}

class SUV implements Car {
    @Override
    public void drive() { System.out.println("驾驶SUV"); }
}

// 抽象工厂
abstract class CarFactory {
    public abstract Car createCar(); // 工厂方法(核心)
}

// 具体工厂
class SedanFactory extends CarFactory {
    @Override
    public Car createCar() { return new Sedan(); }
}

class SUVFactory extends CarFactory {
    @Override
    public Car createCar() { return new SUV(); }
}

// 使用
public class Client {
    public static void main(String[] args) {
        CarFactory factory = new SedanFactory();
        Car car = factory.createCar();
        car.drive(); // 输出:驾驶轿车
        // 如有其它新增汽车类,只需实现Car接口和继承CarFactory类,而无需修改它们
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值