文章目录
引言
在软件开发的世界中,对象的创建可能是一个复杂且重复的过程。为了简化这个过程,设计模式中的"工厂方法"就像一个小工厂,专门负责生产特定类型的对象。今天,我们来深入探索这个设计模式,看看它是如何让对象的创建变得简单又有趣。
工厂方法模式简介
工厂方法模式是一种创建型设计模式。 它通过使用一个工厂类来创建对象,而不是直接使用 new 运算符。这使得程序可以在不知道对象确切类型的情况下,生成对象的实例。
定义与用途:
- 工厂设计模式是一种创建型设计模式,广泛应用于JDK和像Spring、Struts这样的框架中。
- 当有一个超类和多个子类,并且基于输入返回其中一个子类时,使用工厂设计模式是非常合适的。
实现方式:
通过将类的实例化过程从客户端代码转移到工厂类,从而减少客户端的复杂性。
使用场景
- 当类的实例化过程复杂,需要依据不同的条件来创建不同的对象时。
- 当需要解耦对象的创建和使用时。
- 在提供一个类库,并希望只暴露接口而非实现细节时。
优势与劣势
- 优势
提高灵活性:可以在运行时选择创建哪个类的实例。
降低耦合度:客户端代码与具体类的创建过程解耦。
易于扩展:添加新的类不影响现有代码。 - 劣势
增加代码复杂性:可能会引入许多新类,增加系统的复杂性。
维护难度:当添加新类型时,可能需要修改工厂类。
工厂模式在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上,方便学习,欢迎指点:
代码地址