java设计模式学习之【工厂模式】

文章目录

引言

在软件开发的世界中,对象的创建可能是一个复杂且重复的过程。为了简化这个过程,设计模式中的"工厂方法"就像一个小工厂,专门负责生产特定类型的对象。今天,我们来深入探索这个设计模式,看看它是如何让对象的创建变得简单又有趣。

工厂方法模式简介

工厂方法模式是一种创建型设计模式。 它通过使用一个工厂类来创建对象,而不是直接使用 new 运算符。这使得程序可以在不知道对象确切类型的情况下,生成对象的实例。

定义与用途:

  1. 工厂设计模式是一种创建型设计模式,广泛应用于JDK和像Spring、Struts这样的框架中。
  2. 当有一个超类和多个子类,并且基于输入返回其中一个子类时,使用工厂设计模式是非常合适的。

实现方式:

通过将类的实例化过程从客户端代码转移到工厂类,从而减少客户端的复杂性。

使用场景

  • 当类的实例化过程复杂,需要依据不同的条件来创建不同的对象时。
  • 当需要解耦对象的创建和使用时。
  • 在提供一个类库,并希望只暴露接口而非实现细节时。

优势与劣势

  • 优势
    提高灵活性:可以在运行时选择创建哪个类的实例。
    降低耦合度:客户端代码与具体类的创建过程解耦。
    易于扩展:添加新的类不影响现有代码。
  • 劣势
    增加代码复杂性:可能会引入许多新类,增加系统的复杂性。
    维护难度:当添加新类型时,可能需要修改工厂类。

工厂模式在spring中的应用

Spring框架广泛地应用了工厂模式,这是Spring框架中对象管理和依赖注入核心功能的基础。以下是Spring框架中使用工厂模式的几个关键地方:

BeanFactory:
Spring框架中最基本的容器,它提供了依赖注入(DI)的支持。
BeanFactory 使用工厂模式来实例化应用程序中的所有bean。
它使用 getBean 方法来创建bean实例。

ApplicationContext:
它是 BeanFactory 的子接口,提供了更高级的特性,如事件传播、资源加载等。
ApplicationContext 本身也是一个大型工厂,用于创建并管理应用程序中的beans,以及提供对不同类型的bean的访问。

FactoryBean:
Spring中特殊的bean类型,用于产生其他bean实例。
这种模式允许用户实现复杂的初始化逻辑,并通过Spring容器进行管理。

BeanDefinition:
在Spring中,BeanDefinition 代表了bean的配置元数据,它将如何在Spring容器中创建bean的细节描述了出来。
通过这种方式,Spring使用工厂模式来创建具体的bean实例。

单例模式与工厂模式的结合:
默认情况下,Spring容器中的所有bean都是单例的,这意味着每个bean都是全局唯一的并且在整个应用程序中共享。
Spring容器作为工厂,管理着这些单例bean的生命周期和实例化过程。

依赖注入(DI):
虽然DI不是工厂模式,但它利用了工厂模式的概念来实现对象的创建和依赖的注入。
DI容器(如 Ap	plicationContext)负责创建对象和管理它们的依赖关系,这在本质上是一种工厂模式的应用。

电费计算示例(简单工厂模式)

我们将创建一个名为 Plan 的抽象类以及继承该抽象类的具体类。下一步是定义一个名为 GetPlanFactory 的工厂类。

GenerateBill 类将使用 GetPlanFactory 来获取一个 Plan 对象。它将传递信息(DOMESTICPLAN / COMMERCIALPLAN / INSTITUTIONALPLAN)给 GetPlanFactory,以获取它所需的对象类型。

步骤 1: 创建抽象计划类

首先,我们定义一个抽象类 Plan,它包含了计算电费所必需的方法和属性。

java 复制代码
abstract class Plan {
    protected double rate;
    abstract void getRate();

    public void calculateBill(int units) {
        System.out.println(units * rate);
    }
} //end of Plan class.

步骤 2: 定义具体计划类

接着,我们创建具体类 DomesticPlan、CommercialPlan 和 InstitutionalPlan,这些类继承自 Plan 并提供了 getRate 方法的具体实现。

java 复制代码
class DomesticPlan extends Plan {
    public void getRate() {
        rate = 3.50;
    }
} //end of DomesticPlan class.

class CommercialPlan extends Plan {
    public void getRate() {
        rate = 7.50;
    }
} //end of CommercialPlan class.

class InstitutionalPlan extends Plan {
    public void getRate() {
        rate = 5.50;
    }
} //end of InstitutionalPlan class.

步骤 3: 创建工厂类

GetPlanFactory 是一个工厂类,根据传入的计划类型生成相应的计划对象。

java 复制代码
class GetPlanFactory {
    public Plan getPlan(String planType) {
        if (planType == null) {
            return null;
        }
        if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
            return new DomesticPlan();
        } else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
            return new CommercialPlan();
        } else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
            return new InstitutionalPlan();
        }
        return null;
    }
} //end of GetPlanFactory class.

步骤 4: 生成账单

GenerateBill 类使用 GetPlanFactory 来获取具体的计划对象,并根据使用的单位数计算电费。

java 复制代码
class GenerateBill {
    public static void main(String args[]) throws IOException {
        GetPlanFactory planFactory = new GetPlanFactory();

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        System.out.print("Enter the name of plan for which the bill will be generated: ");
        String planName = br.readLine();
        System.out.print("Enter the number of units for bill will be calculated: ");
        int units = Integer.parseInt(br.readLine());

        Plan p = planFactory.getPlan(planName);
        System.out.print("Bill amount for " + planName + " of " + units + " units is: ");
        p.getRate();
        p.calculateBill(units);
    }
} //end of GenerateBill class.

以上就是一个简单的工厂模式示例代码,运行代码我们可以看到:

输入相应的计划就可以算出该类型下具体的电费。

改善为方法工厂模式

当新的计划类型增加时,GetPlanFactory 就需要修改。这违反了开闭原则(对扩展开放,对修改封闭)。

以上举例属于简单工厂模式接下来改为方法工厂模式。

改造步骤

  • 创建一个抽象的工厂类或接口。
  • 为每种计划类型创建具体的工厂类,继承自抽象工厂类或实现工厂接口。
  • 每个具体工厂类实现创建相应产品对象的方法。
java 复制代码
public abstract class GetPlanFactoryPro {
    abstract Plan getPlan();
}

class DomesticPlanFactory extends GetPlanFactoryPro {
    @Override
    Plan getPlan() {
        return new DomesticPlan();
    }
}

class CommercialPlanFactory extends GetPlanFactoryPro {
    @Override
    Plan getPlan() {
        return new CommercialPlan();
    }
}

class InstitutionalPlanFactory extends GetPlanFactoryPro {
    @Override
    Plan getPlan() {
        return new InstitutionalPlan();
    }
}

调用方法:

java 复制代码
public class GenerateBillPro {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        System.out.print("Enter the name of plan for which the bill will be generated: ");
        String planName = br.readLine();
        System.out.print("Enter the number of units for bill will be calculated: ");
        int units = Integer.parseInt(br.readLine());

        GetPlanFactoryPro factory = getFactory(planName);
        if (factory == null) {
            System.out.println("Invalid Plan Type");
            return;
        }

        Plan p = factory.getPlan();
        System.out.print("Bill amount for " + planName + " of " + units + " units is: ");
        p.getRate();
        p.calculateBill(units);
    }

    private static GetPlanFactoryPro getFactory(String planType) {
        if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
            return new DomesticPlanFactory();
        } else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
            return new CommercialPlanFactory();
        } else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
            return new InstitutionalPlanFactory();
        }
        return null;
    }

}

**灵活性和扩展性:**通过使用工厂方法模式,我们增加了代码的灵活性和扩展性。如果要添加新的计划类型,只需增加一个新的工厂类,而无需修改现有的工厂逻辑或客户端代码。

**符合开闭原则:**工厂方法模式使得我们的代码更好地符合开闭原则,因为现在系统可以在不修改现有代码的情况下引入新类型的Plan。

缺点:

随着产品类的增加,相关的工厂类也会增加,可能导致系统类的数量增长。

代码地址

23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址

https://github.com/RuofeiSun/lf-23Pattern

相关推荐
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫3 小时前
泛型(2)
java
超爱吃士力架3 小时前
邀请逻辑
java·linux·后端
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白664 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶4 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb