【软件设计模式】工厂方法与抽象工厂

发布于:2025-08-16 ⋅ 阅读:(18) ⋅ 点赞:(0)

前言:上学期学了一门课——《软件设计模式与体系结构》,当时觉得没什么用,我实际开发中也没使用过,但是刚好报了软件设计师考试,其中也有这部分知识,同时也为了期末考,也就认真学了这门课,理论学的挺6的,发现还挺有意思,刚好最近在优化我的项目,其中也使用到了一些设计模式,想着再系统地复习一遍,记录一下笔记。

一、工厂方法模式

1.概念

        工厂方法模式(Factory Method Pattern)是一种类创建型模式,其核心思想是将对象的实例化延迟到子类。通过定义一个创建对象的接口,但由子类决定具体实例化哪个类。

        工厂方法模式可以简单理解为:“让专门的‘工厂’来生产‘产品’,你只需要告诉工厂你要什么,不用管具体怎么做出来”

打个生活中的比方:
        你去咖啡店买咖啡(这就是 “你” 作为客户端),咖啡店(相当于 “抽象工厂”)有不同的制作区域:美式咖啡区(具体工厂 A)、拿铁区(具体工厂 B)。
        你不需要自己动手做咖啡(不用关心咖啡的具体制作步骤),只需要告诉店员你要美式还是拿铁,对应的制作区就会给你做好(具体工厂生产具体产品)。
        如果后来咖啡店新增了卡布奇诺(新的产品),只需要再开一个卡布奇诺制作区(新的具体工厂),原来的区域和你点单的方式都不用变。

        核心就是:把 “创建东西” 的工作交给专门的 “工厂”,你只需要用,不用知道东西是怎么造出来的。而且以后想换品种,直接换个工厂就行,不用改自己的代码

核心目标

  • 解耦对象的创建与使用,客户端无需关心具体产品类的实例化细节。

  • 支持扩展,符合开闭原则(新增产品时无需修改原有代码)。

2.工厂方法模式的结构

工厂方法模式包含以下4个角色:

角色 职责 示例(书写工具案例)
抽象产品(Product) 定义产品的规范,描述通用功能。 WritingTool(书写工具接口)
具体产品(ConcreteProduct) 实现抽象产品的接口,提供具体功能。 PenBallPenBrushPen
抽象工厂(AbstractFactory) 声明工厂方法(如createProduct()),用于创建产品对象。 WritingToolFactory
具体工厂(ConcreteFactory) 实现抽象工厂的工厂方法,返回具体产品的实例。 PenFactoryBallPenFactory

典型代码结构

抽象工厂类

public interface LoggerFactory {
    Logger createLogger(); // 工厂方法
}

具体工厂类

public class FileLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new FileLogger(); // 返回具体产品
    }
}

3.工厂方法模式的优缺点

优点 缺点
1. 隐藏实例化细节:客户端只依赖抽象接口,无需关心具体类。 1. 类数量增加:每新增一个产品需新增一个工厂类,导致系统复杂度上升。
2. 符合开闭原则:新增产品时只需扩展子类,无需修改原有代码。 2. 理解难度提高:引入了抽象层,增加了系统设计复杂性。
3. 灵活扩展:支持动态切换产品(如通过配置文件)。

4.适用环境

  1. 客户端无需知道具体类名:只需通过工厂接口获取对象(如日志记录器、数据库连接器等)。

  2. 需要动态扩展产品:系统未来可能新增产品类型(如新增日志存储方式)。

  3. 解耦需求:避免客户端代码与具体类直接耦合,提高代码可维护性。

典型场景

  • 日志记录器(文件日志、数据库日志)。

  • 跨平台UI组件(不同操作系统下的按钮、窗口)。

  • 支付系统(支付宝、微信支付等不同支付方式)。

5.工厂方法模式的应用实例

说明:工厂方法模式就像不同的咖啡制作工坊,每个工坊专注于制作一种咖啡。

// 抽象产品:咖啡
public interface Coffee {
    // 制作咖啡的方法
    void make();
}

// 具体产品:拿铁咖啡
public class LatteCoffee implements Coffee {
    @Override
    public void make() {
        System.out.println("制作一杯拿铁咖啡");
    }
}

// 具体产品:美式咖啡
public class AmericanCoffee implements Coffee {
    @Override
    public void make() {
        System.out.println("制作一杯美式咖啡");
    }
}

// 抽象工厂:咖啡工厂
public interface CoffeeFactory {
    // 生产咖啡的方法
    Coffee createCoffee();
}

// 具体工厂:拿铁咖啡工厂
public class LatteCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

// 具体工厂:美式咖啡工厂
public class AmericanCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建美式咖啡工厂
        CoffeeFactory americanFactory = new AmericanCoffeeFactory();
        // 制作美式咖啡
        Coffee americanCoffee = americanFactory.createCoffee();
        americanCoffee.make();  // 输出:制作一杯美式咖啡
        
        // 创建拿铁咖啡工厂
        CoffeeFactory latteFactory = new LatteCoffeeFactory();
        // 制作拿铁咖啡
        Coffee latteCoffee = latteFactory.createCoffee();
        latteCoffee.make();  // 输出:制作一杯拿铁咖啡
    }
}

在这个例子中:

  • 每个咖啡种类(美式、拿铁)是具体产品
  • 每个咖啡工厂只生产一种咖啡
  • 客户端只需选择相应的工厂,无需知道咖啡的具体制作过程
  • 如果要新增咖啡种类,只需添加新的具体产品和对应的工厂

6.总结

  • 核心思想延迟实例化到子类,通过抽象接口隔离客户端与具体产品。

  • 关键设计原则:依赖倒置原则(DIP)、开闭原则(OCP)。

  • 适用性:适合需要灵活扩展、解耦的场景,但需权衡类数量增加的代价。

通过工厂方法模式,可以构建高扩展性、低耦合的系统架构,尤其适合框架设计和中间件开发。

二、抽象工厂模式

1.概念

        抽象工厂模式(Abstract Factory Pattern)是一种对象创建型模式,用于创建一系列相关或相互依赖的对象(即一个产品族),而无需指定它们的具体类。

        抽象工厂模式可以简单理解为:“一个‘超级工厂’,专门生产一系列配套的产品,这些产品天生就适合搭配使用”

打个生活比方:
        就像买电脑时,你可以选 “Intel 套装” 或 “AMD 套装”——

  • Intel 套装里的 CPU、主板、显卡都是 Intel 体系的,它们彼此兼容;
  • AMD 套装里的 CPU、主板、显卡都是 AMD 体系的,也能完美搭配。

        你不用自己逐个挑零件(不用担心买的 CPU 和主板不兼容),只需告诉商家要哪个 “套装系列”,商家就会给你一套能一起用的配件。如果以后出了新的 “ARM 套装”,商家只需新增这个系列的生产能力,你买的方式还是一样的。

        核心就是:用一个工厂生产一整套 “互相匹配” 的产品,你要啥系列就选啥工厂,不用操心内部零件是否兼容

核心目标

  • 封装一组具有共同主题的独立产品

  • 确保客户端始终使用同一产品族中的对象

  • 支持新增产品族(符合开闭原则),但不支持新增产品类型(违反开闭原则)。

2.抽象工厂模式的结构

抽象工厂模式包含以下4个角色:

角色 职责 示例(界面皮肤库)
抽象产品(AbstractProduct) 定义一类产品的接口(如按钮、文本框)。 ButtonTextField
具体产品(ConcreteProduct) 实现抽象产品接口,属于同一产品族(如Spring风格按钮、Summer风格文本框)。 SpringButtonSummerTextField
抽象工厂(AbstractFactory) 声明一组创建产品的方法,每个方法对应一个产品等级结构。 SkinFactory
具体工厂(ConcreteFactory) 实现抽象工厂的方法,生成同一产品族的所有具体产品。 SpringSkinFactorySummerSkinFactory

典型代码结构

抽象工厂接口

public interface SkinFactory {
    Button createButton();
    TextField createTextField();
    ComboBox createComboBox();
}

具体工厂类

public class SpringSkinFactory implements SkinFactory {
    @Override
    public Button createButton() { return new SpringButton(); }
    @Override
    public TextField createTextField() { return new SpringTextField(); }
    // ...其他产品
}

3.抽象工厂模式的优缺点

优点 缺点
1. 隔离具体类:客户端仅依赖抽象接口,与具体实现解耦。 1. 难以扩展新产品类型:新增产品等级需修改所有工厂类,违反开闭原则。
2. 保证产品族一致性:客户端始终使用同一主题的产品。 2. 系统复杂度高:需设计大量接口和类。
3. 支持新增产品族:扩展新主题(如新增Winter风格)无需修改已有代码。

4.适用环境

  1. 系统需要一组相关产品:如UI主题(所有组件风格一致)、跨平台套件(Windows/Mac按钮+菜单)。

  2. 产品族稳定性高:产品等级结构(如按钮、文本框)不会频繁变更。

  3. 隐藏具体实现:客户端只需关注抽象接口,不关心具体产品的创建细节。

典型场景

  • 跨平台应用:同一功能在不同操作系统下的实现(如Windows/Linux的窗口和按钮)。

  • 游戏风格切换:不同主题的游戏场景(如科幻、中世纪风格的武器和地图)。

5.与工厂方法模式对比

对比维度 工厂方法模式 抽象工厂模式
核心目标 创建单一产品 创建一组相关产品(产品族)
工厂职责 一个工厂生产一种产品 一个工厂生产一族产品
扩展性 支持新增产品类型(开闭原则) 仅支持新增产品族(倾斜性开闭原则)
  • 工厂方法模式聚焦于 “单一产品的灵活创建”,适合产品类型单一但实现多样的场景,扩展简单。
  • 抽象工厂模式聚焦于 “相关产品族的整体创建”,适合产品之间有依赖关系的场景,能保证产品兼容性,但扩展新的产品类型较复杂。

6.抽象工厂模式的应用实例

说明:抽象工厂模式就像不同品牌的电脑厂商,每个厂商生产一系列配套的电脑部件。

// 抽象产品:CPU
public interface Cpu {
    void run();
}

// 具体产品:Intel CPU
public class IntelCpu implements Cpu {
    @Override
    public void run() {
        System.out.println("Intel CPU 运行中");
    }
}

// 具体产品:AMD CPU
public class AmdCpu implements Cpu {
    @Override
    public void run() {
        System.out.println("AMD CPU 运行中");
    }
}

// 抽象产品:主板
public interface Mainboard {
    void connect();
}


// 具体产品:Intel主板
public class IntelMainboard implements Mainboard {
    @Override
    public void connect() {
        System.out.println("Intel主板 连接组件");
    }
}

// 具体产品:AMD主板
public class AmdMainboard implements Mainboard {
    @Override
    public void connect() {
        System.out.println("AMD主板 连接组件");
    }
}

// 抽象工厂:电脑配件工厂
public interface ComputerFactory {
    Cpu createCpu();
    Mainboard createMainboard();
}

// 具体工厂:Intel工厂
public class IntelFactory implements ComputerFactory {
    @Override
    public Cpu createCpu() {
        return new IntelCpu();
    }
    
    @Override
    public Mainboard createMainboard() {
        return new IntelMainboard();
    }
}

// 具体工厂:AMD工厂
public class AmdFactory implements ComputerFactory {
    @Override
    public Cpu createCpu() {
        return new AmdCpu();
    }
    
    @Override
    public Mainboard createMainboard() {
        return new AmdMainboard();
    }
}

// 客户端
public class ComputerClient {
    public static void main(String[] args) {
        // 创建Intel工厂,生产Intel系列配件
        ComputerFactory intelFactory = new IntelFactory();
        Cpu intelCpu = intelFactory.createCpu();
        Mainboard intelMainboard = intelFactory.createMainboard();
        intelCpu.run();          // 输出:Intel CPU 运行中
        intelMainboard.connect(); // 输出:Intel主板 连接组件
        
        // 创建AMD工厂,生产AMD系列配件
        ComputerFactory amdFactory = new AmdFactory();
        Cpu amdCpu = amdFactory.createCpu();
        Mainboard amdMainboard = amdFactory.createMainboard();
        amdCpu.run();            // 输出:AMD CPU 运行中
        amdMainboard.connect();   // 输出:AMD主板 连接组件
    }
}

在这个例子中:

  • 每个品牌工厂(Intel、AMD)生产一系列配套产品(CPU 和主板)
  • 同一工厂生产的产品保证兼容
  • 客户端只需选择品牌工厂,就能获得一套兼容的产品
  • 如果要新增品牌,只需添加新的具体工厂和对应的一系列产品

7.总结

  • 核心思想封装产品族的创建,确保客户端代码与具体产品解耦。

  • 关键设计原则:依赖倒置原则(DIP)、开闭原则(部分支持)。

  • 适用性:适合产品族稳定但需灵活切换主题的场景,但需权衡扩展新产品类型的成本。

通过抽象工厂模式,可以高效管理多组相关对象的创建,尤其适用于需要强一致性主题化的系统设计。


网站公告

今日签到

点亮在社区的每一天
去签到