设计模式——工厂模式
介绍
工厂模式(Factory Pattern)是 Java 设计模式中的一种创建型模式,它提供了一种创建对象的方式,而不需要指定具体的类。工厂模式通过引入工厂类来创建对象,将对象的创建过程从具体的类中分离出来,从而提高了代码的灵活性和可扩展性。
实现
简单工厂模式
首先来看简单工厂模式(这个从严格意义上说并不属于23中设计模式中的一种,因为这样设计违反了 开闭原则
)
结构
- 工厂类(Factory):负责创建不同类型的产品对象。
- 产品接口(Product):定义产品的基本功能。
- 具体产品(ConcreteProduct):实现了产品接口,定义具体的产品功能。
示例代码:
// 产品接口
public interface Product {
void use();
}
// 具体产品
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using Product A");
}
}
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using Product B");
}
}
// 工厂类
public class SimpleFactory {
public Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
}
return null;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
SimpleFactory factory = new SimpleFactory();
Product productA = factory.createProduct("A");
productA.use();
Product productB = factory.createProduct("B");
productB.use();
}
}
优缺点
- 优点:
- 客户端代码只需要依赖工厂类,不需要关心产品的具体实现。
- 可以集中管理和创建对象的逻辑,便于扩展。
- 缺点:
- 违反了单一职责原则,工厂类的职责过重。
- 当产品种类增加时,工厂类需要修改,违背了开闭原则。
适用场景
- 产品种类固定且较少时使用简单工厂模式比较合适。
这里简单补充一下 开闭原则
开闭原则(Open/Closed Principle,OCP) 是面向对象设计中的五大基本原则之一,也是 SOLID 原则中的第二个原则。开闭原则的核心思想是:
“软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。”
简单来说,开闭原则要求我们在软件开发过程中,通过增加新代码来扩展系统功能,而不是修改现有的代码。这样做的目的是减少对已有功能的影响,避免引入新的错误,并提高代码的可维护性和可扩展性。
显然,上面的简单工厂模式就违背了开闭原则:比如,当我们新增产品 C 时,工厂中的逻辑代码则需要进行修改,而不是保障原有的代码不变,新增代码,所以违背了开闭原则
工厂方法模式
工厂方法模式将简单工厂模式中的工厂类的职责分散到各个子类中,每个子类负责创建一种具体产品。客户端只需要知道具体工厂类,而不需要知道具体的产品类。
结构
- 抽象工厂类(Creator):声明一个工厂方法,返回一个产品对象。
- 具体工厂类(ConcreteCreator):实现工厂方法,返回具体的产品对象。
- 产品接口(Product):定义产品的基本功能。
- 具体产品类(ConcreteProduct):实现产品接口,定义具体的产品功能
示例代码:
// 产品接口
public interface Product {
void use();
}
// 具体产品A
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using Product A");
}
}
// 具体产品B
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using Product B");
}
}
// 抽象工厂类
public abstract class Creator {
public abstract Product factoryMethod();
}
// 具体工厂A
public class ConcreteCreatorA extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}
// 具体工厂B
public class ConcreteCreatorB extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
Product productA = creatorA.factoryMethod();
productA.use();
Creator creatorB = new ConcreteCreatorB();
Product productB = creatorB.factoryMethod();
productB.use();
}
}
这里我们可以清晰的看到,每个具体的产品都有一个具体的工厂,需要哪种具体的实例,只需要找到对应的具体的工厂获取即可。但是,同时也会有一个问题:如果有大量的具体产品类,那么就需要大量的具体工厂类,可能会导致 “类的爆炸”(这里只是形象的表述一下,但它可以被理解为由于类的数量过多而导致的代码库复杂性增加和维护难度加大的问题)
优缺点
- 优点:
- 客户端只需要知道工厂方法,而不需要了解产品的具体类。
- 符合开闭原则,当需要添加新的产品时,只需要增加新的工厂类和产品类,不需要修改现有代码。
- 缺点:
- 当产品种类非常多时,可能需要创建大量的具体工厂类。
适用场景
- 需要通过子类决定产品的创建方式时,适合使用工厂方法模式。
- 当产品种类比较多,且需要对不同产品使用不同的创建方式时,工厂方法模式会更加灵活。
抽象工厂模式
抽象工厂模式提供了一个接口,用于创建一系列相关或相互依赖的对象。每个具体工厂类都可以创建一组相关的产品,但它们的具体产品是不同的。抽象工厂模式适用于创建多个相关产品族的情况。
结构
- 抽象工厂接口(AbstractFactory):声明创建一组产品的方法。
- 具体工厂类(ConcreteFactory):实现抽象工厂接口,创建一组具体产品。
- 抽象产品接口(AbstractProduct):声明产品的基本功能。
- 具体产品类(ConcreteProduct):实现抽象产品接口,定义具体的产品功能。
示例代码:
// 抽象产品A
public interface AbstractProductA {
void use();
}
// 抽象产品B
public interface AbstractProductB {
void use();
}
// 具体产品A1
public class ConcreteProductA1 implements AbstractProductA {
@Override
public void use() {
System.out.println("Using ConcreteProductA1");
}
}
// 具体产品A2
public class ConcreteProductA2 implements AbstractProductA {
@Override
public void use() {
System.out.println("Using ConcreteProductA2");
}
}
// 具体产品B1
public class ConcreteProductB1 implements AbstractProductB {
@Override
public void use() {
System.out.println("Using ConcreteProductB1");
}
}
// 具体产品B2
public class ConcreteProductB2 implements AbstractProductB {
@Override
public void use() {
System.out.println("Using ConcreteProductB2");
}
}
// 抽象工厂
public interface AbstractFactory {
AbstractProductA createProductA();
AbstractProductB createProductB();
}
// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB2();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
AbstractProductA productA1 = factory1.createProductA();
productA1.use();
AbstractProductB productB1 = factory1.createProductB();
productB1.use();
AbstractFactory factory2 = new ConcreteFactory2();
AbstractProductA productA2 = factory2.createProductA();
productA2.use();
AbstractProductB productB2 = factory2.createProductB();
productB2.use();
}
}
简单解读一下:
我们有产品A和产品B两个大类,产品A中又有两个A系列的产品 A1 和 A2,产品B中又有B系列的产品 B1 和 B2,如果按照前面说工厂方法模式,就需要创建四个具体产品类工厂,未免有些麻烦。仔细看一看,这几个产品都是有关系的,由于产品都有 1号 和 2号 两个不同型号的产品,所以我们可以设计两个工厂(当然,需要先设计一个抽象工厂,这里就不多说了),一个主要生产 A1 和 B1 产品的工厂,一个主要生产 A2 和 B2 产品的工厂,这样设计看着就比较合理了。
其实我个人认为,由于具体产品之间有着某种关系,借助这种关系,我们可以将工厂方法模式进而改造成这种抽象工厂模式(个人见解,可能有些许错误,望某位大佬指点一下)
优缺点
- 优点:
- 客户端可以通过抽象工厂接口创建一系列相关的产品对象,避免了对具体类的依赖。
- 符合开闭原则,能够很容易扩展新的产品族。
- 缺点:
- 增加了类的数量和复杂度。
- 当需要扩展产品种类时,可能需要修改多个工厂类和产品类。
适用场景
- 当系统需要提供多个产品族,并且每个产品族中的产品需要一起使用时,适合使用抽象工厂模式。
- 当系统中有多个系列的产品需要创建,且这些产品之间有依赖关系时,抽象工厂模式是非常有用的。
总结
三个工厂模式对比如下
在使用的时候,应当结合具体情况,选择最为合适的工厂模式进行设计