工厂(Factory)模式
介绍
(1) 工厂模式的定义
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
(2) 具体实现方式
按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。
应用场景
工厂模式适用于需要封装复杂的对象创建逻辑、统一管理对象创建、定制化创建对象、隐藏对象实现细节以及支持对象扩展和变化的场景。它提供了一种灵活、可扩展和可维护的对象创建方式,同时也符合面向对象设计的开闭原则。
(1) 对象的创建逻辑比较复杂
如果对象的创建过程包含复杂的判断逻辑、依赖关系或者多个步骤,可以使用工厂模式将对象的创建逻辑封装起来,简化客户端的代码。
(2) 需要统一管理对象的创建
如果系统中的对象需要通过统一的入口进行创建和管理,可以使用工厂模式。工厂模式可以集中管理对象的创建过程,避免了对象的随意创建和管理,提高了代码的可维护性和可读性。
(3) 需要对对象进行定制化创建
如果对象的创建需要根据不同的需求进行定制化配置,可以使用工厂模式。工厂模式可以根据客户端的请求参数或配置信息,选择性地创建不同类型或配置的对象。
(4) 需要隐藏对象的具体实现细节
如果希望客户端代码与具体的对象实现解耦,只关注对象的接口而不关心具体的实现细节,可以使用工厂模式。工厂模式可以封装对象的创建过程,隐藏具体实现细节,提供给客户端一个统一的接口。
(5) 需要实现对象的扩展和变化
如果系统中的对象需要支持扩展和变化,可以使用工厂模式。工厂模式可以通过添加新的具体工厂类或修改现有的工厂类来创建新的对象类型,而不需要修改客户端代码。
(6) 总结
工厂模式又可分为简单工厂模式、工厂方法模式、抽象工厂模式,它们分别的应用场景又有所不同,详情见各个工厂模式的具体介绍。
- 简单工厂模式适用于对象创建逻辑简单且客户端只关注抽象接口的场景;
- 工厂方法模式适用于对象创建逻辑复杂、需要根据具体需求选择不同产品的场景;
- 抽象工厂模式适用于创建一组相关或依赖的产品对象,需要在运行时刻动态选择具体工厂的场景。选择合适的模式取决于具体的需求和设计目标。
各工厂模式的区别
(1) 简单工厂与工厂方法模式的区别
-
简单工厂模式中包含判断创建什么对象的逻辑,工厂方法模式则需要调用者判断要实例化什么具体类型的工厂进而创建出想要的对象;
-
当增加新类时,简单工厂模式需要修改工厂类,而工厂方法模式不需要,因此工厂方法模式遵守了开闭原则,而简单工厂模式没遵守;
-
简单工厂模式因为采用了静态方法,所以不利于继承,而工厂方法模式恰恰相反,需要利用到继承来从抽象工厂中派生出各种各样的具体工厂。
(2) 工厂方法与抽象工厂模式的不同
抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成。
但抽象工厂一个工厂不止生产一类产品,也就是说中方法个数不同,抽象产品的个数也不同。
简单工厂(Simple Factory)模式
简单工厂模式(Simple Factory Pattern)
介绍
(1) 模式定义
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它通过一个工厂类来封装对象的创建逻辑,根据客户端的需求返回相应的产品实例。
简单工厂模式的工厂类通常为静态方法,所以简单工厂模式又称静态工厂模式。
模式角色
(1) 工厂类(Factory)
负责创建对象的类,它通常是一个静态类,包含一个用于创建对象的静态方法,该方法根据客户端传递的参数决定创建哪种具体产品。
(2) 抽象产品类(Abstract Product)
定义了产品的抽象特性,它是具体产品类的父类或接口。
(3) 具体产品类(Concrete Product)
实现抽象产品类中定义的抽象方法,是由工厂类创建的具体对象。
代码示例
package com.creational;
public class SimpleFactoryPattern {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");//工厂类创建产品A
productA.use();
Product productB = SimpleFactory.createProduct("B");//工厂类创建产品B
productB.use();
}
}
//具体产品类的父类,可以为抽象类或接口
abstract class Product {
public abstract void use();
}
//各个产品的ConcreteProduct类负责创建具体产品
class ConcreteProductA extends Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
class ConcreteProductB extends Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
//工厂类负责创建产品的逻辑
class SimpleFactory {
//使用静态方法,这样就可以不用初始化工厂也可以创建产品
public static Product createProduct(String type) {
Product product = null;
if (type.equals("A")) {
product = new ConcreteProductA();
} else if (type.equals("B")) {
product = new ConcreteProductB();
} else {
//若要添加具体产品,还要增加判断逻辑和产品创建代码
}
return product;
}
}
应用场景
(1) 对象的创建逻辑相对简单
当需要创建的对象的创建逻辑相对简单,只涉及一些基本的条件判断或实例化操作时,可以使用简单工厂模式。
(2) 客户端只需要关注抽象接口
当客户端只需要知道对象的抽象接口而无需了解具体实现类时,可以使用简单工厂模式。工厂类封装了对象的创建逻辑,客户端只需通过工厂类获取所需的对象。
优缺点
(1) 简单工厂模式的优点
- 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。可以免除客户端直接创建产品对象的职责;
- 客户端无需知道所创建具体产品的类名,只需知道参数即可;
(2) 简单工厂模式的缺点
- 工厂类集中了所有产品的创建逻辑,职责过重,一旦异常,整个系统将受影响;
- 违背“开闭原则”,导致系统扩展困难,一旦增加新产品不得不修改工厂类的逻辑,在产品类型较多时,可能造成逻辑过于复杂;
- 简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构。
简单工厂模式与开闭原则
简单工厂模式并不完全符合开闭原则,但在某些情况下可以近似地满足开闭原则。
开闭原则(Open-Closed Principle)是面向对象设计原则之一,它强调对于扩展是开放的,对于修改是关闭的。即系统的设计应该是可扩展的,新的功能应该通过添加新的代码来实现,而不是修改现有的代码。
然而,简单工厂模式在添加新的产品类型时需要修改工厂类的代码,违反了开闭原则。每次添加新的产品类型,都需要修改工厂类的创建逻辑,这导致了工厂类的修改。
尽管如此,简单工厂模式的变动范围相对较小,仅限于工厂类的修改,对于客户端代码的修改是最小化的。这意味着在客户端代码中使用简单工厂模式的地方,不需要修改现有的代码,只需要修改工厂类的代码来适应新的产品类型。
因此,虽然简单工厂模式不完全符合开闭原则,但它可以在一定程度上近似满足开闭原则。然而,在需要频繁添加新的产品类型时,可能会导致工厂类的逻辑复杂化,不利于维护和扩展。在这种情况下,可以考虑使用工厂方法模式或抽象工厂模式,以更好地支持开闭原则。
工厂方法(Factory Method)模式
工厂方法模式(Factory Method)-最易懂的设计模式解析
介绍
工厂方法模式(Factory Method Pattern)定义了一个用于创建对象的接口,但将具体对象的创建延迟到子类中进行。这样可以使得子类决定实例化哪个具体类。
工厂方法模式的核心思想是将对象的创建封装在工厂方法中,由具体的子类来实现工厂方法,从而创建不同类型的对象。工厂方法模式符合开闭原则,即对扩展开放,对修改关闭。
模式角色
(1) 抽象工厂(Abstract Factory)
所有具体工厂的父类。提供了创建产品的接口。
(2) 具体工厂(Concrete Factory)
主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
(3) 抽象产品(Abstract Product)
所有具体产品的父类。定义了产品的规范,描述了产品的主要特性和功能。
(4) 具体产品(ConcreteProduct)
实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
代码示例
public class FactoryMethodPattern {
public static void main(String[] args) {
// 先创建工厂A
MethodFactory factoryA = new ConcreteFactoryA();
// 工厂A中创建产品A
Product productA = factoryA.createProduct();
// 使用产品A
productA.use();
MethodFactory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.use();
}
}
// 所有具体工厂的父类
abstract class MethodFactory {
public abstract Product createProduct();
}
// 具体工厂类负责创建产品
class ConcreteFactoryA extends MethodFactory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类负责创建产品
class ConcreteFactoryB extends MethodFactory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
应用场景
(1) 对象的创建逻辑复杂或多样化
当对象的创建逻辑比较复杂,涉及多个变种或条件判断时,可以使用工厂方法模式。每个具体工厂类负责创建特定类型的对象,可以灵活地处理复杂的创建逻辑。
(2) 客户端需要根据具体需求选择不同的产品
当客户端需要根据自身需求选择不同类型的产品对象时,可以使用工厂方法模式。客户端通过与特定工厂类交互,根据需求获取所需的产品对象。
优缺点
(1) 工厂方法模式优点
- 符合开闭原则:工厂方法模式将具体产品的创建延迟到了具体工厂类中进行,当需要增加新的产品时,只需要增加相应的具体产品类和具体工厂类,不需要修改已有的代码,符合开闭原则。
- 降低耦合度:工厂方法模式将客户端与具体产品的创建过程解耦,客户端只需要知道抽象产品类和抽象工厂类即可,具体产品的创建由具体工厂类完成,降低了客户端与具体产品的耦合度。
- 更好的扩展性:由于工厂方法模式将具体产品的创建分散到多个具体工厂类中,每个工厂类只负责创建一个具体产品对象,因此扩展性更好。
(2) 工厂方法模式缺点
- 增加了类的个数:使用工厂方法模式,需要定义抽象产品类和抽象工厂类,每增加一个具体产品类,就需要增加一个具体工厂类,因此会增加类的个数。
- 增加了系统的抽象性和理解难度:工厂方法模式引入了抽象层,增加了系统的抽象性和理解难度。
抽象工厂(Abstract Factory)模式
介绍
工厂方法模式中每个工厂只创建一类具体类的对象,这将会导致系统当中的工厂类过多,这势必会增加系统的开销。
于是可以考虑将一些相关的具体产品类组成一个“产品类族”,由同一个工厂来统一生产,也就是“抽象工厂模式”的基本思想。
模式角色
(1) 抽象工厂(Abstract Factory)
提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同的产品。
(2) 具体工厂(Concrete Factory)
主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
(3) 抽象产品(Product)
定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
(4) 具体产品(ConcreteProduct)
实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
代码示例
package com.creational.factory;
/**
* 使用到了简单工厂模式中的部分代码
*/
public class AbstractFactoryPattern {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
Product productA = factory1.createProductA();
productA.use();
Product productB = factory1.createProductB();
productB.use();
}
}
// 提供创建产品的接口,包含多个创建产品的方法
abstract class AbstractFactory {
public abstract Product createProductA();
public abstract Product createProductB();
}
//实际工厂类负责创建产品的逻辑
class ConcreteFactory1 extends AbstractFactory {
@Override
public Product createProductA() {
return new ConcreteProductA();
}
@Override
public Product createProductB() {
return new ConcreteProductB();
}
}
应用场景
(1) 需要创建一组相关或依赖的产品对象
当需要创建一组相关或依赖的产品对象时,可以使用抽象工厂模式。抽象工厂定义了一组创建产品对象的接口,每个具体工厂类负责创建特定系列的产品对象。
(2) 需要在运行时刻动态选择具体工厂
当需要在运行时刻动态选择具体工厂来创建产品对象时,可以使用抽象工厂模式。这样可以根据不同的环境或配置,选择不同的具体工厂来创建不同类型的产品对象。
优缺点
(1) 抽象工厂模式的优点
- 由于每个产品族的创建由一个工厂负责,从而可以保证产品之间的兼容性,避免了不同产品之间的矛盾。
- 可以在不修改客户端代码的情况下更换产品族,从而满足客户端的不同需求。
- 符合开闭原则,当需要增加新的产品族时,只需要增加相应的抽象工厂和具体工厂即可,不需要修改已有的代码。
(2) 抽象工厂模式的缺点
- 由于增加了抽象工厂和具体工厂,增加了系统的复杂度。
- 当需要增加新的产品等级结构时,需要修改所有的抽象工厂和具体工厂类,不符合开闭原则。
注:同一个工厂生产的是同一个产品族的不同等级。