一、设计模式介绍
设计模式是我们开发中常常需要面对的核心概念,它们是解决特定问题的模板或者说是经验的总结。这些模式被设计出来是为了让软件设计更加清晰、代码更加可维护且能应对未来的变化。良好的设计模式不仅能解决重复代码的问题,还能使团队中的每个成员都能理解并遵循统一的开发方式。
使用良好的设计模式可以显著提高软件项目的质量和开发效率。它们帮助开发者抽象复杂度,通过预定义的架构方式简化系统结构的设计和实现。设计模式还提供了一种通用语言,使得开发者在讨论架构问题时能够迅速地达成共识,同时确保代码的健壁性和可扩展性。不论是在创建对象、组织复杂的类结构,还是处理类与类之间的关系时,设计模式都提供了有效的管理和优化策略,使得软件更加稳健,易于管理和扩展。
下面是常见的 23 种设计模式:
本文详细介绍了设计模式中的工厂模式,旨在为软件开发人员提供清晰的理解和应用指南。工厂模式是一种创建型设计模式,用于在不直接指定具体类的情况下创建对象,从而增强代码的灵活性和可扩展性。我们探讨了工厂模式的三种主要形式:简单工厂模式、工厂方法模式和抽象工厂模式。每种模式都通过其结构、代码示例和类图进行了解释,以展示它们在实际开发中如何减少系统的依赖性、提高可维护性和支持易变需求。此外,本文还讨论了每种模式的优势、适用场景以及它们在现实世界软件开发中的实际应用,帮助开发者在面对不同的编程挑战时,能选择合适的模式以优化设计和实现过程。
二、工厂模式概述
工厂模式(Factory Pattern)是一种常用的创建型设计模式,其核心目的是实现创建对象的接口和具体的实例化分离,通过建立一个工厂类,对实现了同一接口的一些类进行实例的创建,以增加系统的灵活性和可维护性。
当需要大量创建一个类的实例的时候,可以使用工厂模式,即从原生的使用类的构造去创建对象的形式迁移到基于工厂提供的方法去创建对象的形式。基于工厂模式的实例创建具有以下好处:
- 大批量创建对象的时候有统一的入口,易于代码维护;
- 当发生修改时,仅需修改工厂类的创建方法即可;
- 符合现实世界的模式,即由工厂来制作产品(对象);
本文将通过类图及示例代码详细介绍工厂模式的三种形式:简单工厂模式、工厂方法模式 和 抽象工厂模式。
三、简单工厂模式
简单工厂模式是一种创建型设计模式,它提供了一个创建对象的接口,让其子类决定实例化哪一个类。该模式将对象的创建集中管理,通过专门的工厂类来负责创建所有实例,这样可以避免在客户代码中重复创建对象,降低系统的耦合度,提高模块的可复用性。
1. 简单工厂模式类图
以下类图展示了简单工厂模式的结构:
在这个类图中,Client
不直接与具体产品类(ConcreteProductA
和 ConcreteProductB
)交互,而是通过 Factory
类来进行。这样做的优点是客户端从具体产品的创建过程中解耦,提高了程序的扩展性和可维护性。
2. 简单工厂模式示例代码
下面的示例代码进一步阐释了简单工厂模式的应用:
- 抽象产品:定义了产品的接口,所有的产品都必须实现这个接口。
interface Product {
void use();
}
- 具体产品:实现了产品接口的实体类,定义了具体产品的行为。
class ConcreteProductA implements Product {
public void use() {
System.out.println("Using ConcreteProductA");
}
}
class ConcreteProductB implements Product {
public void use() {
System.out.println("Using ConcreteProductB");
}
}
- 简单工厂:一个工厂类,提供了一个创建对象的方法,客户端通过调用这个方法并传入参数来创建不同的产品实例。
class Factory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
}
return null;
}
}
- 调用简单工厂示例:客户端使用
Factory
类创建具体的产品实例。这种方式的主要优点是工厂类包含必要的逻辑判断,可以决定在运行时创建哪个产品的实例。由于使用了工厂类,客户端可以完全从具体产品的实现中解耦,只需要关心产品的接口。
public class Client {
public static void main(String[] args) {
Product product = Factory.createProduct("A");
product.use();
}
}
通过这种方式,简单工厂模式使得新增产品类时,只需要扩展工厂类而不需要修改现有的客户代码,从而增加了系统的灵活性。
也就是将"A"换成其他产品,如果产品类不存在,我们需要新增产品类,同时添加createProduct里type为新产品的情况,其实还是有点麻烦的,所有下面的工厂方法就是对此的改进。
四、工厂方法模式
工厂方法模式是一种创建型设计模式,它提供了一个创建对象的方法,但实际的工作将由子类完成,这样的设计模式可以让类的实例化推迟到其子类中进行。这种模式通过定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类,从而使代码管理更加简单,同时也扩展了系统的可能功能。
1. 工厂方法模式类图
下面的类图描述了工厂方法模式的结构:
在这个模式中,Creator
类定义了一个抽象的 factoryMethod()
,该方法需要被子类实现以返回一个 Product
类的实例。这样,客户端代码只依赖于 Product
接口,而具体实例化哪个产品类由具体的 Creator
子类决定,这样提高了系统的灵活性和扩展性。
2. 工厂方法模式示例代码
以下示例代码进一步阐释了工厂方法模式的应用:
- 产品接口:定义了所有具体产品需要实现的接口。
interface Product {
void use();
}
- 具体产品:实现了产品接口的具体类,这些类定义了具体产品的行为。
class ConcreteProductA implements Product {
public void use() {
System.out.println("Using ConcreteProductA");
}
}
class ConcreteProductB implements Product {
public void use() {
System.out.println("Using ConcreteProductB");
}
}
- 抽象创建者:定义了抽象的工厂方法,由具体创建者来实现。
abstract class Creator {
abstract Product factoryMethod();
}
- 具体创建者:继承抽象创建者,实现了工厂方法,决定实际生产的产品。
class ConcreteCreatorA extends Creator {
@Override
Product factoryMethod() {
return new ConcreteProductA();
}
}
class ConcreteCreatorB extends Creator {
@Override
Product factoryMethod() {
return new ConcreteProductB();
}
}
- 客户端使用:客户端代码通过调用工厂方法来获取产品对象,而无需知道具体的产品类。
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreteCreatorA();
Product product = creator.factoryMethod();
product.use();
}
}
通过这种方式,工厂方法模式使得添加新产品类时,只需添加一个具体的创建者而无需修改现有代码,符合开闭原则,提高了代码的可维护性和扩展性。
与简单工厂加个产品需要改一揽子的代码相比,工厂方法只需要创建一个新的创建者类,客户端调用时ConcreteCreatorA更换为新类名就可以。
五、抽象工厂模式
抽象工厂模式是创建型设计模式中的一种,它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。这种模式是工厂方法模式的一种扩展,它用于创建产品家族,而不仅仅是单一产品。通过这种方式,可以增强程序的灵活性和可维护性,同时也支持良好的封装性和扩展性。
1. 抽象工厂模式类图
以下类图清晰展示了抽象工厂模式的结构和组件之间的关系:
在这个模式中,AbstractFactory
是一个抽象类,定义了创建一系列产品的接口。每个具体工厂类(如 ConcreteFactory1
和 ConcreteFactory2
)实现这个接口,生产具体的产品实例。客户端通过使用工厂接口,而不是直接实例化产品,从而使得具体的产品在客户端保持抽象状态。
2. 抽象工厂模式示例代码
以下示例代码详细说明了抽象工厂模式的实现:
- 产品接口:定义了产品的操作。
interface AbstractProductA {
void use();
}
interface AbstractProductB {
void use();
}
- 具体产品:实现产品接口的具体类。
class ConcreteProductA1 implements AbstractProductA {
public void use() {
System.out.println("Using ConcreteProductA1");
}
}
class ConcreteProductA2 implements AbstractProductA {
public void use() {
System.out.println("Using ConcreteProductA2");
}
}
class ConcreteProductB1 implements AbstractProductB {
public void use() {
System.out.println("Using ConcreteProductB1");
}
}
class ConcreteProductB2 implements AbstractProductB {
public void use() {
System.out.println("Using ConcreteProductB2");
}
}
- 抽象工厂:定义了创建一系列产品的方法。
abstract class AbstractFactory {
abstract AbstractProductA createProductA();
abstract AbstractProductB createProductB();
}
- 具体工厂:实现抽象工厂,定义了生产具体产品的方式。
class ConcreteFactory1 extends AbstractFactory {
@Override
AbstractProductA createProductA() {
return new ConcreteProductA1();
}
@Override
AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
class ConcreteFactory2 extends AbstractFactory {
@Override
AbstractProductA createProductA() {
return new ConcreteProductA2();
}
@Override
AbstractProductB createProductB() {
return new ConcreteProductB2();
}
}
- 客户端使用:客户端通过抽象工厂接口创建并使用产品。
public class Client {
public static void main(String[] args) {
AbstractFactory factory = new ConcreteFactory1();
AbstractProductA productA = factory.createProductA();
AbstractProductB productB = factory.createProductB();
productA.use();
productB.use();
}
}
通过这种方式,抽象工厂模式允许系统在不具体指定产品的情况下创建一系列相关或依赖的对象,支持配置多个工厂,每个工厂创建不同的产品实例,从而增强系统的灵活性和扩展性。
这也是工厂方法模式的升级,虽然在开闭原则上做出了让步,但是为更强的灵活和扩展创造了条件。
六、三种工厂模式异同
首先,我们来看三种工厂模式的定义:
- 简单工厂模式:最简单的工厂模式,适合产品种类较少且不经常变化的情况。工厂类集中处理所有产品的创建逻辑,这简化了代码但降低了系统的扩展性。
- 工厂方法模式:允许多个创建者子类来实现具体的产品创建。这种分散的创建过程提高了系统的灵活性,并且增强了代码的可扩展性。
- 抽象工厂模式:提供一个创建一系列相关或互依赖对象的接口,而无需指定它们具体的类。适用于处理多个系列产品的情况,每个具体工厂都能生产一个完整的产品家族。
接着,我们用一张表来总结三种工厂模式的特性差异:
特点/模式 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
---|---|---|---|
定义 | 提供一个创建对象的接口,由接口决定创建哪一种产品类的实例。 | 定义一个用于创建对象的接口,但由子类决定要实例化的类是哪一个。 | 提供一个接口,用于创建相关的对象家族,而不需要明确指定具体类。 |
对象创建的责任 | 集中于一个工厂类,通过接收特定参数决定创建对象的类型。 | 分散到多个子类中,每个子类封装了一个产品的创建过程。 | 集中于多个工厂类,每个工厂类负责创建一个产品家族。 |
实现复杂度 | 低 | 中 | 高 |
灵活性 | 低,增加新产品需要修改工厂类 | 高,每个产品有其对应的具体工厂类 | 非常高,支持创建产品家族且易于添加新的产品组合 |
产品扩展性 | 较差,扩展新产品时可能需要修改工厂逻辑 | 好,扩展新产品时仅需添加相应的具体工厂类 | 优,允许通过添加具体工厂和产品类的方式来扩展产品家族 |
应用场景 | 适用于产品类型数量不多且变动不频繁的情况 | 适用于一个类族有多个变体,但仅需使用其一的情况 | 适用于需要创建多系列产品或产品族,且产品族需要一起使用的情况 |
通过上述表格,我们可以清晰地看到每种工厂模式的特点及其适用的场景。在不同的开发需求下,我们需要选择最合适的模式。
七、工厂模式的优势与应用场景
1. 工厂模式的优势
优势 | 说明 |
---|---|
封装性 | 用户仅需知道产品对应的工厂名,无需关心产品的具体创建细节,从而降低了系统的复杂度。 |
扩展性 | 新增产品时,只需扩展一个工厂类,而不需修改现有代码,符合开闭原则,易于系统扩展和维护。 |
解耦 | 分离了产品的实例化过程,使用抽象层促使产品的实现和使用分离,提高了系统的灵活性和可维护性。 |
代码可维护性 | 由于工厂类和产品类的职责清晰,系统的不同部分可以独立变化而互不影响,从而提高代码的可维护性。 |
2. 工厂模式的应用场景
- 当系统中的产品有多于一个的产品族,而系统只消费其中某一产品族时。
- 当属于同一个产品族的一组产品被设计用于一起使用时,可以使用抽象工厂模式。
- 当系统需要被配置成一个具有多个不同行为的产品组成时,可以使用工厂模式确保其灵活性和可扩展性。
八、工厂模式总结
工厂模式是设计模式中非常关键的一类,主要用于解决对象创建过程中的复杂性,并帮助将对象的实例化过程与使用者分离,从而增加系统的整体封装性和灵活性。通过定义一个用于创建对象的接口,允许子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类。这种模式不仅帮助管理和维护大型软件系统中的对象创建问题,也使系统更加模块化,易于理解和扩展。总的来说,工厂模式在需要生成复杂对象时,提供了极大的便利和高效的管理与维护策略。