工厂模式-工厂方法模式

1 工厂方法概述

工厂方法模式(Factory Method Pattern) 将对象的创建延迟到子类中实现,每个具体产品类都有一个对应的工厂类负责创建,从而使得系统更加灵活。客户端可以通过调用工厂方法来创建所需的产品,而不必关心具体的实现细节。这种模式符合开放-封闭原则,对扩展开放、对修改关闭。

2 工厂方法模式 与 简单工厂模式 的区别

不同于 简单工厂模式 定义一个工厂方法来创建产品,工厂方法模式每个产品都有一个对应的工厂类来创建。这样每个产品类和工厂类相互独立,可以独立进行扩展,符合开放-封闭原则。例如,在一个图形绘制软件中,可以使用 工厂方法模式 来创建不同类型的图形对象,如圆形、矩形、三角形等,每个图形都有一个对应的工厂类来创建。

假设我们要设计一个电子产品工厂来生产不同类型的产品,如手机、电视、电脑等。如果使用 简单工厂模式,我们需要一个工厂类来创建所有不同类型的电子产品,客户端代码需要指定电子产品类型,工厂类根据类型来创建相应的产品对象。但是,随着产品类型的增加,工厂类的代码会越来越复杂,难以维护和扩展

相反,如果使用 工厂方法模式,每个产品类型都有一个对应的工厂类来创建,工厂方法 可以独立进行扩展,每个工厂类只需要关心自己负责的产品类型,代码更加清晰和易于维护。例如,手机工厂类负责创建手机产品对象,电视工厂类负责创建电视产品对象,电脑工厂类负责创建电脑产品对象等。客户端代码只需要知道所需的产品类型,选择对应的工厂类即可,不需要知道具体的创建细节。

3 模式详细说明

首先看 工厂方法模式 的类图:
在这里插入图片描述
从上面的类图,可以知道简单工厂模式包含下面的角色:

(1) IProduct: 产品的接口定义。

(2) ConcreteProductX: IProduct 的具体实现类。

(3) IProductFactory: 产品工厂接口定义。

(4) ProductXFactory: IProductFactory 的具体实现类,用于生产对应的 ConcreteProductX

(5) ProductFactoryProvider: 根据传入的工厂类型,返回对应的产品工厂实例。

(6) Client: 客户类,它不会直接依赖 ConcreteProductX,而是依赖 IProduct,它使用 ProductFactory 创建 IProduct 的具体实例。

4 工厂方法模式例子

IProduct接口

interface IProduct {
    void operation();
}

ConcreteProductX 产品的具体实现类

/**
 * 具体产品A类
 */
class ConcreteProductA implements IProduct {
    @Override
    public void operation() {
        System.out.println("I am product A");
    }
}
/**
 * 具体产品B类
 */
class ConcreteProductB implements IProduct {
    @Override
    public void operation() {
        System.out.println("I am product B");
    }
}
/**
 * 具体产品C类
 */
class ConcreteProductC implements IProduct {
    @Override
    public void operation() {
        System.out.println("I am product C");
    }
}

IProductFactory 工厂定义接口

/**
 * 产品工厂类接口定义
 */
interface IProductFactory {
    IProduct createProduct();
}

ProductXFactory 工厂具体实现类

/**
 * 生产A产品的工厂类
 */
class ProductAFactory implements IProductFactory {
    @Override
    public IProduct createProduct() {
        return new ConcreteProductA();
    }
}
/**
 * 生产B产品的工厂类
 */
class ProductBFactory implements IProductFactory {
    @Override
    public IProduct createProduct() {
        return new ConcreteProductB();
    }
}
/**
 * 生产C产品的工厂类
 */
class ProductCFactory implements IProductFactory {
    @Override
    public IProduct createProduct() {
        return new ConcreteProductC();
    }
}

ProductFactoryProvider 产品工厂实例提供者

使用反射技术,根据传入的 工厂全局限定名 生成对应的工厂实例:

/**
 * 产品工厂实例提供者
 */
class ProductFactoryProvider {
    /**
     * 记录 工厂全局限定名 与 工厂实例 的对应关系
     * Key:工厂类全局限定名
     * value: 工厂实例
     */
    private static Map<String, IProductFactory> factoryMap = new HashMap<>();

    /**
     * 获取工厂实例
     * @param factoryClassName 工厂全局限定名
     * @return 工厂实例
     */
    public synchronized static IProductFactory getProductFactory(String factoryClassName) {
        // 如果 factoryMap 包含要获取的工厂类的全局限定名,则直接返回
        if (factoryMap.containsKey(factoryClassName)) {
            return factoryMap.get(factoryClassName);
        }
        // 创建工厂实例,并存到 factoryMap 中
        try {
            IProductFactory productFactory = (IProductFactory) Class.forName(factoryClassName).newInstance();
            factoryMap.put(factoryClassName, productFactory);
            return productFactory;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

Client 使用简单工厂生成产品的客户类

public class Client {
    public static void main(String[] args) throws Exception {
        List<IProduct> productList = new ArrayList<>();
        productList.add(ProductFactoryProvider
                .getProductFactory("com.design.pattern.ProductAFactory")
                .createProduct());
        productList.add(ProductFactoryProvider
                .getProductFactory("com.design.pattern.ProductBFactory")
                .createProduct());
        productList.add(ProductFactoryProvider
                .getProductFactory("com.design.pattern.ProductCFactory")
                .createProduct());

        for (IProduct product : productList) {
            product.operation();
        }
    }
}

5 工厂方法模式在 slf4j 库中的应用

在日常开发过程中,我们经常会使用 slf4j + log4j 作为平台的日志框架,里面的 LoggerFactory 的设计就是使用了 工厂方法模式

其中,slf4j 定义了 org.slf4j.Logger 接口,它有以下实现类:

  • org.slf4j.helpers.NOPLogger: NOP 代表“no operation”(无操作)。它的作用是提供一个空的日志记录器实现,即它不会执行任何实际的日志记录操作。
  • org.slf4j.helpers.SubstituteLogger: 用于替换真实的日志记录器实现,以便在测试环境中进行测试。SubstituteLogger 记录所有的日志记录请求,但不会将它们发送到真实的日志记录器实现,而是存储在内存中。在测试过程中,测试人员可以通过访问 SubstituteLogger 的内部状态来检查哪些日志记录请求被发起,以及它们的参数和级别。
  • org.apache.logging.slf4j.Log4jLogger: Log4j 的 Logger 实现。

每个 Logger 都有一个对应的 Factory 类,这些 Factory 类都是 org.slf4j.ILoggerFactory 的实现类,Logger 与 LoggerFactory 的类图(只列个大概,大家有兴趣可以自己再细看)如下:

在这里插入图片描述

6 总结

工厂方法模式 的优点包括:

  • 通过将对象的创建过程抽象出来,使得客户端代码可以更加清晰地表达其意图,并且与具体产品的类名解耦,从而提高了代码的灵活性和可维护性。
  • 工厂方法模式可以支持多态性,客户端代码可以通过使用抽象工厂接口,以一种统一的方式使用不同的具体工厂和产品,从而减少了代码的重复。
  • 工厂方法模式可以支持扩展性,通过添加新的具体工厂和产品,可以方便地扩展应用程序的功能,而无需修改现有的代码。

工厂方法模式 的缺点包括:

  • 工厂方法模式需要客户端代码实例化具体的工厂对象,这可能会导致代码的复杂性和耦合性增加。
  • 工厂方法模式会增加应用程序的类的数量,因为需要为每个具体产品创建一个具体工厂,这可能会使代码变得复杂。
  • 工厂方法模式可能会导致性能问题,因为它需要额外的对象创建和管理,可能会影响应用程序的性能。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值