工厂模式

工厂模式是一种创建对象的设计模式,将对象的创建和使用分离,可提高代码可维护性、可扩展性,封装复杂创建逻辑。其目的是隐藏对象创建的过程与细节。

在这里插入图片描述

工厂模式经典案例

国际化案例,根据用户的IP返回不同语言的界面。

在这里插入图片描述

工厂模式分类与优缺点

工厂模式主要分为三类:

一、简单工厂模式

简单工厂模式又称为静态工厂方法模式。它定义了一个创建对象的类,由这个类来封装实例化对象的行为。在简单工厂模式中,工厂类有一个创建产品对象的方法,这个方法可以根据传入的参数决定创建哪种具体的产品对象。

优点:实现了对象创建和使用的分离,客户端无需了解对象的具体创建过程,只需要关心如何使用对象。代码结构简单,容易理解和实现。

缺点:不符合开闭原则,即当需要增加新的产品对象时,需要修改工厂类的代码。

二、工厂方法模式

工厂方法模式是在简单工厂模式的基础上,将工厂类的创建方法抽象成抽象方法,由具体的工厂子类实现这些抽象方法来创建具体的产品对象。

优点:符合开闭原则,当需要增加新的产品对象时,只需要增加一个具体的工厂子类,无需修改原有代码。

缺点:工厂子类过多时,会导致代码结构复杂,增加系统的维护成本

三、抽象工厂模式

抽象工厂模式中,工厂类不再负责创建具体的产品对象,而是负责创建一系列相关的产品对象。客户端只需要调用抽象工厂的抽象创建方法,由具体的工厂子类来创建所需的产品对象系列。

优点:代码更加灵活和可维护,当需要增加新的产品对象系列时,只需要增加一个具体的工厂子类,无需修改原有代码。

缺点:实现复杂,不适合简单的应用场景。对开闭原则的支持也有一定的局限性,当需要增加新的产品对象种类时,需要修改抽象工厂和所有具体工厂的代码。

工厂模式的使用场景

基础使用:

  • 当对象创建涉及多步骤及依赖关系,工厂模式可封装复杂创建逻辑,让调用方代码更简洁。
  • 需根据不同条件创建不同类型对象,工厂模式能动态确定创建逻辑。
  • 期望统一管理对象创建以利维护和扩展,工厂模式可集中处理创建过程。

高级使用:

  • 与其他模式混用,如工厂模式与策略模式结合,或工厂模式、策略模式、模板模式共同使用。

工厂模式的优点

一、解耦

  • 工厂模式将对象的创建和使用分离,使得代码的各个部分之间的耦合度降低。使用者无需了解对象的具体创建过程,只需要从工厂获取所需的对象即可。这样,当对象的创建逻辑发生变化时,不会影响到使用对象的代码部分。
  • 例如,在一个电商系统中,订单模块需要使用支付对象进行支付操作。如果不使用工厂模式,订单模块可能直接实例化支付对象,这样订单模块就与具体的支付对象创建过程紧密耦合。当支付方式发生变化时,订单模块的代码需要进行相应的修改。而使用工厂模式,订单模块只需要从支付工厂获取支付对象,无需关心支付对象的具体创建过程,从而实现了解耦。

二、代码复用

  • 工厂类可以被多个地方复用,提高了代码的复用性。工厂类通常封装了对象的创建逻辑,可以根据不同的条件创建不同类型的对象。这些创建逻辑可以在多个地方重复使用,避免了代码的重复编写。
  • 例如,在一个图形绘制系统中,有多种不同类型的图形(圆形、矩形、三角形等)需要创建。可以创建一个图形工厂类,该工厂类可以根据不同的参数创建不同类型的图形对象。这样,在多个绘图模块中都可以复用这个图形工厂类,提高了代码的复用性。

三、符合迪米特法则和单一职责原则

  • 迪米特法则也称为最少知识原则,它强调一个对象应该对其他对象有尽可能少的了解。工厂模式将对象的创建逻辑封装在工厂类中,使用者只需要与工厂类进行交互,无需了解对象的具体创建过程,符合迪米特法则。
  • 单一职责原则要求一个类应该只有一个引起它变化的原因。工厂类的职责就是创建对象,它将对象的创建逻辑集中在一个地方,使得代码的职责更加清晰,符合单一职责原则。
  • 例如,在一个企业管理系统中,员工对象的创建可能涉及到从数据库中读取员工信息、验证员工数据的合法性等复杂的操作。如果将这些创建逻辑分散在各个使用员工对象的模块中,会导致代码的职责不清晰,违反单一职责原则。而使用工厂模式,将员工对象的创建逻辑封装在员工工厂类中,使得代码的职责更加清晰,符合单一职责原则。

简单工厂模式实现

以下是一个用于创建不同类型的手机。

一、定义手机产品接口

/**
 * 手机接口
 */
public interface Phone {
    void call();

    void sendMessage();
}

二、创建具体手机产品类

/**
 * 安卓手机
 */
public class AndroidPhone implements Phone {
    @Override
    public void call() {
        System.out.println("Android手机正在通话。");
    }

    @Override
    public void sendMessage() {
        System.out.println("Android手机正在发送消息。");
    }
}


/**
 * 苹果手机
 */
public class IPhone implements Phone {
    @Override
    public void call() {
        System.out.println("iPhone正在通话。");
    }

    @Override
    public void sendMessage() {
        System.out.println("iPhone正在发送消息。");
    }
}

三、创建简单工厂类

public class SimplePhoneFactory {
    /**
     * 根据传入的手机类型决定创建哪种具体的手机对象
     */
    public static Phone createPhone(String phoneType) {
        if ("android".equals(phoneType)) {
            return new AndroidPhone();
        } else if ("iphone".equals(phoneType)) {
            return new IPhone();
        } else {
            return null;
        }
    }
}

四、使用简单工厂-测试

public class SimpleFactoryTest {
    public static void main(String[] args) {
        Phone androidPhone = SimplePhoneFactory.createPhone("android");
        if (androidPhone != null) {
            androidPhone.call();
            androidPhone.sendMessage();
        }
        System.out.println("--------------------------------");
        Phone iPhone = SimplePhoneFactory.createPhone("iphone");
        if (iPhone != null) {
            iPhone.call();
            iPhone.sendMessage();
        }
    }
}

执行结果

Android手机正在通话。
Android手机正在发送消息。
--------------------------------
iPhone正在通话。
iPhone正在发送消息。

总结

简单工厂模式成功地实现了手机的创建与使用之间的解耦。这使得使用者无需操心手机具体的创建流程,仅需从工厂获取所需的手机即可。然而,此模式存有一定弊端。后续若要增添新的手机类型,就必须持续地对工厂类进行修改,如此一来,维护成本便会相对较高。在简单工厂模式里,需依据方法的输入,编写大量的 if-else 语句来判断并创建不同的对象。

工厂方法模式实现

工厂方法模式可视为简单工厂模式的升级版。它针对简单工厂模式在扩展时需改动工厂代码的缺点,对工厂类进行了优化改进。通过这种方式,工厂方法模式有效地解决了简单工厂模式在扩展性方面的不足。

例子还是使用上面的Phone,实现方法如下。

一、定义手机产品接口

代码和简单工厂模式一样。

二、创建具体手机产品类

代码和简单工厂模式一样。

三、定义抽象工厂接口

public interface PhoneFactory {
    Phone createPhone();
}

四、创建具体工厂类

public class AndroidPhoneFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new AndroidPhone();
    }
}

public class IPhoneFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new IPhone();
    }
}

五、使用工厂方法模式

public class FactoryMethodTest {
    public static void main(String[] args) {
        PhoneFactory androidPhoneFactory = new AndroidPhoneFactory();
        Phone androidPhone = androidPhoneFactory.createPhone();
        androidPhone.call();
        androidPhone.sendMessage();
        System.out.println("--------------------------------");
        PhoneFactory iPhoneFactory = new IPhoneFactory();
        Phone iphone = iPhoneFactory.createPhone();
        iphone.call();
        iphone.sendMessage();
    }
}

执行结果

Android手机正在通话。
Android手机正在发送消息。
--------------------------------
iPhone正在通话。
iPhone正在发送消息。

总结

其结果与简单工厂模式相同。二者的主要区别在于:简单工厂模式仅有一个工厂类,负责创建所有产品对象;而工厂方法模式则演变为多个工厂,每个工厂对应一种特定的产品创建。这样一来,后续扩展时只需实现工厂接口即可创建新的产品对象。然而,该模式的缺点是随着产品种类的增加,工厂类的数量也会越来越多。

抽象工厂模式实现

抽象工厂模式可视为工厂方法模式的一种扩展。该模式主要用于解决一类产品的创建问题。在抽象工厂模式中,工厂类的职责并非创建具体的单一产品对象,而是负责创建一系列相关的产品对象。例如,在某些场景下,工厂不仅要创建手机,还需创建与之对应的手机配件。对比工厂方法模式,最直观的区别在于工厂方法模式中的PhoneFactory接口通常只有一个创建手机的方法,而抽象工厂模式的AbstractPhoneFactory接口则拥有多个方法,分别用于创建不同的相关产品对象。

下面是简单画的图。

在这里插入图片描述

一、定义手机产品接口

上面有代码。

二、创建具体手机产品类

上面有代码。

三、定义手机配件接口

public class AndroidCharger implements PhoneAccessory {
    @Override
    public void describe() {
        System.out.println("安卓充电器。");
    }
}

public class IPhoneCharger implements PhoneAccessory {
    @Override
    public void describe() {
        System.out.println("苹果充电器。");
    }
}

五、定义抽象工厂接口

public interface AbstractPhoneFactory {
    /**
     * 创建手机
     */
    Phone createPhone();

    /**
     * 创建配件
     */
    PhoneAccessory createAccessory();
}

六、创建具体工厂类

public class AndroidFactory implements AbstractPhoneFactory {

    @Override
    public Phone createPhone() {
        return new AndroidPhone();
    }

    @Override
    public PhoneAccessory createAccessory() {
        return new AndroidCharger();
    }
}


public class IPhoneFactory implements AbstractPhoneFactory {
    @Override
    public Phone createPhone() {
        return new IPhone();
    }

    @Override
    public PhoneAccessory createAccessory() {
        return new IPhoneCharger();
    }
}

七、使用抽象工厂模式

public class AbstractFactoryTest {
    public static void main(String[] args) {
        AbstractPhoneFactory androidFactory = new AndroidFactory();
        Phone android = androidFactory.createPhone();
        PhoneAccessory androidAccessory = androidFactory.createAccessory();
        android.call();
        android.sendMessage();
        androidAccessory.describe();
        System.out.println("----------------------------------");
        AbstractPhoneFactory iPhoneFactory = new IPhoneFactory();
        Phone iphone = iPhoneFactory.createPhone();
        PhoneAccessory iphoneAccessory = iPhoneFactory.createAccessory();
        iphone.call();
        iphone.sendMessage();
        iphoneAccessory.describe();
    }
}

执行结果

Android手机正在通话。
Android手机正在发送消息。
安卓充电器。
----------------------------------
iPhone正在通话。
iPhone正在发送消息。
苹果充电器。

总结

在抽象工厂模式中,可存在多个方法用于创建同品类的不同对象。然而,该模式存在一定的缺点,即当需要增加新的品类时,需要对所有的具体工厂实现类进行修改。例如,若在抽象工厂接口AbstractFactory中新增一个方法B,那么AndroidFactoryIPhoneFactory等具体工厂类都需要实现这个新增的方法B

在抽象工厂模式中,可以将上述示例中的工厂替换为工作中实际会用到的对象,例如对象存储服务(OSS)。以下展示的仅仅是两个 OSS 工厂,而在实际应用中,可能需要对接众多的 OSS 服务提供商,以满足不同的业务需求。
在这里插入图片描述

Spring中哪些地方使用了工厂模式

在 Spring 框架中,很多地方都使用了工厂模式,以下是一些用到的地方:

一、BeanFactory 和 ApplicationContext

Spring 的核心容器 BeanFactory 和 ApplicationContext 就是工厂模式的典型应用。它们负责创建和管理各种 Bean 对象。当你从容器中获取一个 Bean 时,容器会根据配置信息和需求创建相应的对象并返回给你。

例如,当你在代码中使用@Autowired注解自动注入一个 Bean 时,Spring 容器会在幕后使用工厂模式来创建这个 Bean 对象。

二、Bean 的创建过程
在创建 Bean 的过程中,Spring 使用了工厂模式。对于不同类型的 Bean(如单例 Bean、原型 Bean 等),Spring 会采用不同的策略来创建它们。
例如,对于单例 Bean,Spring 会在容器启动时就创建好,并在后续的请求中重复使用这个对象。而对于原型 Bean,每次请求都会创建一个新的对象。

三、各种内置的 FactoryBean 实现

Spring 提供了很多内置的 FactoryBean 接口的实现,这些实现也使用了工厂模式。例如,JndiObjectFactoryBean用于从 JNDI 中获取对象,ProxyFactoryBean用于创建代理对象等。

四、AOP 动态代理的选择

Spring 的 AOP 代理工厂会根据不同的情况选择不同类型的动态代理。如果目标对象实现了接口,通常会使用 JDK 动态代理;如果目标对象没有实现接口,Spring 会使用 CGLIB 动态代理来创建代理对象。

这种根据不同情况选择合适代理方式的机制也是工厂模式的一种体现,代理工厂根据目标对象的特征来决定创建哪种类型的代理对象,从而实现了灵活的 AOP 功能。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐