云计算百科
云计算领域专业知识百科平台

简单工厂、工厂方法、抽象工厂到底有什么区别?一文讲透工厂模式的演进

引言

很多初学者分不清简单工厂、工厂方法、抽象工厂,其实它们并不是三个孤立的设计模式,而是同一条演进路线:简单工厂 → 工厂方法 → 抽象工厂。简单工厂把创建逻辑抽离,却堆满 if-else;工厂方法把工厂也抽象掉,新增产品不改老代码;抽象工厂则解决一族产品的配套创建问题。每一步升级,都是用更高复杂度换取更强扩展性。

目录

一. 简单工厂

二. 工厂方法

三. 抽象工厂

实战:软考真题

一. 简单工厂

1. 结构图

2. 代码实现

①创建抽象类、具体类

abstract class Product{
public abstract void info();
}

class ProductA extends Product{
@Override
public void info() {
System.out.println("产品的信息:A");
}
}

class ProductB extends Product{
@Override
public void info() {
System.out.println("产品的信息:B");
}
}

②创建工厂类

class Factory{
public static Product createProduct(String type){
Product product = null;
switch (type){
case "A":
product = new ProductA();
break;
case "B":
product = new ProductB();
break;
default:
System.out.println("没有 "+type+" 类型的产品");
break;
}
return product;
}
}

测试一下

//简单工厂模式
public class SimpleFactory {
public static void main(String[] args) {
Product productA = Factory.createProduct("A");
productA.info();
Product productB = Factory.createProduct("B");
productB.info();
Product productC = Factory.createProduct("C");
productC.info();
}
}

运行结果如下:

3. 解读

序号精髓点说明代码体现
1 返回类型是抽象类,实际值是具体类 编译期类型为父类型,运行期类型为子类,本质是多态(向上转型) public static Product createProduct(…) 返回 Product,实际 new ProductA()
2 父 = 子,多态思想 客户端只依赖抽象 Product,不依赖任何具体产品类 Product productA = Factory.createProduct("A");
3 封装创建,客户端不再 new new 被关进工厂,客户端与具体类解耦 客户端只写 Factory.createProduct("A"),不出现 new ProductA()
4 集中判断(switch/if-else) 工厂内部集中决定创建哪个产品,一目了然 switch(type){ case "A": … case "B": … }
5 静态方法,调用方便 不用先创建工厂对象,直接类名调用 public static Product createProduct(…)
6 客户端只认“能干什么”,不认“是谁” 客户端只调抽象方法,具体执行哪个由运行时对象决定 productA.info(); 只调抽象方法
7 switch 是特征,也是软肋 新增产品要改 switch,违反开闭原则,这正是被工厂方法取代的原因 每加一个产品,就要加一个 case
8 静态方法无法被继承重写 简单直接,但工厂无法扩展,这是“简单”的代价 static 方法不能被 override

4. 最大的缺点:违反“开放—封闭”原则

现在我们的工厂类只支持创建A、B两种产品,但是后续加入产品C时,遇到如下问题:

说白了,此时我们由于新加了一个产品C,导致不得不修改工厂类的代码。这的确违反了“开放—封闭”原则。因为我们通常对扩展是开放的,对修改是关闭的。对应到此处就是,你扩展了产品C是没问题的,但是不能修改工厂类的代码。

由于简单工厂模式存在上述缺陷,因此引出下面的工厂方法模式.

二. 工厂方法

1. 结构图

2. 代码实现

①创建产品接口、实现类

interface Product{
public void info();
}

class ProductA implements Product{
@Override
public void info() {
System.out.println("产品的信息:A");
}
}

class ProductB implements Product{
@Override
public void info() {
System.out.println("产品的信息:B");
}
}

②创建工厂接口、实现类

interface Factory{
public Product createProduct();
}

class FactoryA implements Factory{
@Override
public Product createProduct() {
return new ProductA();
}
}

class FactoryB implements Factory{
@Override
public Product createProduct() {
return new ProductB();
}
}

测试一下

//工厂方法
public class test {
public static void main(String[] args) {
Factory factoryA = new FactoryA();
Product productA = factoryA.createProduct();
productA.info();

Factory factoryB = new FactoryB();
Product productB = factoryB.createProduct();
productB.info();
}
}

运行效果:

3. 解读

序号要点说明代码体现
1 工厂也抽象成接口 工厂不再是一个具体类,而是一个接口,定义创建产品的规范 interface Factory { Product createProduct(); }
2 每个产品配一个专属工厂 一个具体产品对应一个具体工厂,职责单一 FactoryA → ProductA,FactoryB → ProductB
3 返回类型是抽象产品,实际值是具体产品 和简单工厂一样,编译期是父类型,运行期是子类,本质是多态 public Product createProduct() 返回 Product,实际 new ProductA()
4 父 = 子,多态思想 客户端只依赖 Product 和 Factory 两个抽象,不依赖具体类 Product productA = factoryA.createProduct();
5 客户端不再 switch 判断 简单工厂的 switch 被“多态分发”取代,选哪个工厂由客户端决定 客户端选 new FactoryA() 还是 new FactoryB()
6 新增产品不改老代码 加一个产品 = 加一个产品类 + 加一个工厂类,符合开闭原则 新增 ProductC 只需加 ProductC + FactoryC
7 代价:类数量翻倍 每个产品都要配一个工厂,类的数量成对增长 N 个产品 → N 个产品类 + N 个工厂类
8 只解决“一个产品等级” 工厂接口里只有一个 createProduct(),搞不定产品族 无法保证“按钮+复选框”配套创建

4. 优点:遵循“开放—封闭”原则

举例:我们新加一个产品C,只需要增加一个对应的专属工厂类即可,而无需修改任何之前的代码(也就是对拓展开放,对修改关闭)

可见,我们新增一个产品时,只需要新增对应的类、具体工厂类即可,而无需修改之前的任何代码,这就遵守了“开放—封闭”原则(即:对新增开放,对修改关闭。说白了就是当新增一部分功能时,可以新增代码,但尽量不要修改之前的代码)。

5. 补充

这个结构图,其实和上面的结构图是一个意思,只不过画的方式有区别,但表达的意思是一样的。

下面的事项看看就行,了解即可。

三. 抽象工厂

1. 结构图

2. 代码实现

// ========== 抽象产品族 ==========

// 产品等级 1:按钮
interface Button {
void render();
}

// 产品等级 2:复选框
interface Checkbox {
void render();
}

// ========== 具体产品:Windows 家族 ==========

class WindowsButton implements Button {
@Override
public void render() {
System.out.println("渲染 Windows 按钮");
}
}

class WindowsCheckbox implements Checkbox {
@Override
public void render() {
System.out.println("渲染 Windows 复选框");
}
}

// ========== 具体产品:Mac 家族 ==========

class MacButton implements Button {
@Override
public void render() {
System.out.println("渲染 Mac 按钮");
}
}

class MacCheckbox implements Checkbox {
@Override
public void render() {
System.out.println("渲染 Mac 复选框");
}
}

// ========== 抽象工厂:定义一族产品的创建接口 ==========

interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}

// ========== 具体工厂:Windows 工厂 ==========

class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}

@Override
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}

// ========== 具体工厂:Mac 工厂 ==========

class MacFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacButton();
}

@Override
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}

// ========== 客户端 ==========

public class AbstractFactoryTest {
public static void main(String[] args) {
// 只选一次工厂,之后创建的所有组件都自动配套
GUIFactory factory = new WindowsFactory();
// GUIFactory factory = new MacFactory(); // 换一行就切换整个产品族

Button button = factory.createButton();
Checkbox checkbox = factory.createCheckbox();

button.render();
checkbox.render();
}
}

3. 解读

序号优点说明代码体现
1 保证产品族内的一致性 同一个工厂创建出的产品天然配套,不会出现“Windows 按钮配 Mac 复选框” WindowsFactory 只会产出 WindowsButton + WindowsCheckbox
2 客户端与具体产品解耦 客户端只依赖抽象 Button、Checkbox、GUIFactory,不碰任何具体类 客户端代码里没有 new WindowsButton()
3 切换产品族非常方便 换一个工厂,整个产品族一起换,只改一行 new WindowsFactory() → new MacFactory()
4 符合开闭原则(对新增产品族而言) 新增一个产品族(如 Linux 风格),只需加一套产品类 + 一个工厂类,不改老代码 新增 LinuxButton、LinuxCheckbox、LinuxFactory
5 职责清晰,符合单一职责 每个具体工厂只负责创建自己那一族产品 MacFactory 只造 Mac 系列
6 便于统一管理与扩展 一族产品的创建逻辑集中在一个工厂里,规范统一 所有 Mac 组件的创建都归 MacFactory

4. 优点

抽象工厂最大的优点:保证一族产品配套一致,且切换产品族只需换一个工厂。

5. 补充

实战:软考真题

题目

以上就是本篇文章的全部内容,喜欢的话可以留个免费的关注呦~~~

赞(0)
未经允许不得转载:网硕互联帮助中心 » 简单工厂、工厂方法、抽象工厂到底有什么区别?一文讲透工厂模式的演进
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!