6、(创建型设计模式)Java 抽象工厂模式深度学习指南

以下是为你精心撰写的 《Java 抽象工厂模式深度学习指南》,专为 Java 后端开发者打造,内容涵盖:定义、作用、与工厂方法模式的深度对比、真实业务场景、实现方式、Spring 集成、避坑指南、最佳实践、面试高频题,所有示例均含中文注释,可直接用于项目重构、多环境配置、多数据库驱动、多租户系统等复杂架构设计。


📘 Java 抽象工厂模式深度学习指南

—— 从“单一产品族”到“多套完整解决方案”的架构跃迁

作者:Java 后端架构实战导师
适用对象:Java 后端开发者(Spring Boot / 微服务 / 多租户 / 多数据库 / 企业级系统)
目标:彻底理解“产品族”概念,掌握一次性创建一组相关或依赖对象的终极工厂模式
核心原则“我要一套完整的解决方案,不是单个零件,而是成套的、能协同工作的整体”


✅ 一、什么是抽象工厂模式?

📌 定义:

抽象工厂模式(Abstract Factory Pattern) 是一种创建型设计模式,它提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类

🔍 核心思想:

  • 不止创建一个对象,而是创建一组对象(产品族)
  • 每个“产品族”中的对象必须协同工作(如:MySQL + MySQLTransaction)
  • 客户端只依赖抽象接口,不依赖具体实现
  • 通过一个工厂接口,统一创建多个相关产品

💡 一句话记忆
“我不是要一个按钮,我要一套完整的 Windows 主题:按钮、菜单、窗口、图标,它们要风格一致。”

🆚 对比:工厂方法 vs 抽象工厂

维度工厂方法模式抽象工厂模式
创建对象数量一次创建一个对象一次创建一组对象(产品族)
关注点“怎么创建一个支付方式?”“怎么创建一套完整的数据库系统?”
接口数量一个工厂接口(PaymentFactory一个抽象工厂接口 + 多个产品接口
扩展维度扩展产品类型(新增支付宝)扩展产品族(新增 PostgreSQL 系统)
典型场景单一产品变体多套完整配置/风格/环境
客户端依赖依赖一个工厂依赖一个抽象工厂,创建多个产品

举个生活例子
你去装修房子:

  • 工厂方法:你问“给我一个门”,工厂给你一扇实木门或防盗门
  • 抽象工厂:你问“给我一套北欧风格装修”,工厂给你:北欧门 + 北欧灯 + 北欧地板 + 北欧沙发 —— 风格统一、配套使用

✅ 二、抽象工厂模式有什么作用?(Why Use Abstract Factory?)

作用说明
创建产品族一次性创建一组相互依赖、风格一致的对象
隔离产品族的实现客户端无需知道具体类,只通过抽象接口操作
支持多环境/多租户如:开发环境用 H2,生产环境用 MySQL,测试环境用 Oracle,整套配置统一切换
符合开闭原则新增一个产品族(如 PostgreSQL 系统),无需修改客户端代码
提高系统一致性避免出现“MySQL 连接 + Oracle 事务”的错误组合
支持热插拔配置通过配置文件切换整套实现,无需改代码
降低耦合度客户端与具体产品实现完全解耦

💡 经典名言
When you need to create families of related or dependent objects, use the Abstract Factory pattern.
——《Design Patterns: Elements of Reusable Object-Oriented Software》


✅ 三、抽象工厂模式与工厂方法模式的深度对比

对比维度工厂方法模式抽象工厂模式
核心目的创建一个对象创建一组对象(产品族)
结构复杂度简单:一个工厂 + 一个产品复杂:一个抽象工厂 + 多个产品接口 + 多个具体工厂
扩展方式新增一个产品类 + 一个工厂类新增一个产品族(多个产品类 + 一个工厂类)
客户端调用factory.createProduct()factory.createProductA(); factory.createProductB();
典型场景支付方式(支付宝/微信)数据库系统(MySQL + MySQLTransaction)
依赖关系产品之间无依赖产品之间强依赖、必须配套
是否适合多环境❌ 仅适合单一产品变体✅ 完美支持多环境/多租户

关键区别图示

工厂方法
PaymentFactoryAlipay
PaymentFactoryWeChatPay

抽象工厂
DatabaseFactoryMySQLFactory(创建 MySQLConnection + MySQLTransaction
DatabaseFactoryPostgreSQLFactory(创建 PostgreSQLConnection + PostgreSQLTransaction

⚠️ 重要结论
如果产品之间是“独立的” → 用工厂方法
如果产品之间是“配套的” → 用抽象工厂


✅ 四、抽象工厂模式的典型使用场景(Java 后端真实案例)

场景说明是否推荐使用抽象工厂模式
多数据库驱动系统MySQL、PostgreSQL、Oracle 的连接 + 事务 + SQL 解析器✅ 强烈推荐
多租户 SaaS 系统不同租户使用不同缓存(Redis vs Memcached)、不同日志系统(ELK vs 文件)✅ 推荐
多 UI 主题Windows 主题、Mac 主题、暗黑模式(按钮、菜单、窗口)✅ 推荐
多消息队列适配RabbitMQ + Kafka + RocketMQ 的连接、序列化、重试策略✅ 推荐
多序列化方式JSON + Protobuf + Hessian 的编码器、解码器、注册器✅ 推荐
多缓存策略本地缓存(Caffeine) + Redis + 多级缓存组合✅ 推荐
多安全认证JWT + OAuth2 + LDAP 的 Token 生成器、校验器、刷新器✅ 推荐
单一支付方式只有支付宝、微信,无配套事务/日志❌ 用工厂方法即可
简单配置对象只有一个 DataSource❌ 用 Spring @Bean 即可

判断标准
“我需要同时创建多个对象,它们必须是同一套、风格一致、协同工作的?”
→ 是 → 用抽象工厂模式!


✅ 五、抽象工厂模式的三种实现方式详解(含中文注释)

我们从基础结构Spring 自动装配,逐步深入。


🔹 1. 基础结构:抽象工厂 + 产品族(经典实现)

/**
 * 【1】产品族1:数据库连接接口
 */
interface Connection {
    void connect(); // 连接数据库
}

/**
 * 【2】产品族2:数据库事务接口
 */
interface Transaction {
    void begin();   // 开始事务
    void commit();  // 提交事务
    void rollback(); // 回滚事务
}

/**
 * 【3】具体产品1:MySQL 连接
 */
class MySQLConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("🔌 MySQL: 成功连接到 MySQL 数据库");
    }
}

/**
 * 【4】具体产品2:MySQL 事务
 */
class MySQLTransaction implements Transaction {
    @Override
    public void begin() {
        System.out.println("🔄 MySQL: 开始事务");
    }

    @Override
    public void commit() {
        System.out.println("✅ MySQL: 提交事务");
    }

    @Override
    public void rollback() {
        System.out.println("❌ MySQL: 回滚事务");
    }
}

/**
 * 【5】具体产品3:PostgreSQL 连接
 */
class PostgreSQLConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("🔌 PostgreSQL: 成功连接到 PostgreSQL 数据库");
    }
}

/**
 * 【6】具体产品4:PostgreSQL 事务
 */
class PostgreSQLTransaction implements Transaction {
    @Override
    public void begin() {
        System.out.println("🔄 PostgreSQL: 开始事务");
    }

    @Override
    public void commit() {
        System.out.println("✅ PostgreSQL: 提交事务");
    }

    @Override
    public void rollback() {
        System.out.println("❌ PostgreSQL: 回滚事务");
    }
}

/**
 * 【7】抽象工厂接口:定义创建产品族的方法
 */
interface DatabaseFactory {
    Connection createConnection(); // 创建连接
    Transaction createTransaction(); // 创建事务
}

/**
 * 【8】具体工厂1:MySQL 工厂
 */
class MySQLFactory implements DatabaseFactory {
    @Override
    public Connection createConnection() {
        return new MySQLConnection(); // 创建 MySQL 连接
    }

    @Override
    public Transaction createTransaction() {
        return new MySQLTransaction(); // 创建 MySQL 事务
    }
}

/**
 * 【9】具体工厂2:PostgreSQL 工厂
 */
class PostgreSQLFactory implements DatabaseFactory {
    @Override
    public Connection createConnection() {
        return new PostgreSQLConnection(); // 创建 PostgreSQL 连接
    }

    @Override
    public Transaction createTransaction() {
        return new PostgreSQLTransaction(); // 创建 PostgreSQL 事务
    }
}

/**
 * 【10】客户端:使用抽象工厂创建整套产品族
 */
public class AbstractFactoryDemo {
    public static void main(String[] args) {
        // 模拟从配置文件读取数据库类型
        String dbType = "mysql"; // 可改为 "postgresql"

        DatabaseFactory factory;
        if ("mysql".equals(dbType)) {
            factory = new MySQLFactory(); // 创建 MySQL 产品族
        } else if ("postgresql".equals(dbType)) {
            factory = new PostgreSQLFactory(); // 创建 PostgreSQL 产品族
        } else {
            throw new IllegalArgumentException("不支持的数据库类型");
        }

        // ✅ 客户端只依赖抽象接口,无需知道具体实现
        Connection conn = factory.createConnection(); // 创建连接
        Transaction tx = factory.createTransaction(); // 创建事务

        // 使用产品族:它们是“配套”的
        conn.connect(); // 连接数据库
        tx.begin();     // 开始事务
        tx.commit();    // 提交事务
        // 输出:
        // 🔌 MySQL: 成功连接到 MySQL 数据库
        // 🔄 MySQL: 开始事务
        // ✅ MySQL: 提交事务

        // ✅ 如果切换为 PostgreSQL,只需改一行代码,其余逻辑完全不变!
    }
}
✅ 优点:
  • 产品族强一致性:MySQL 连接只能搭配 MySQL 事务,杜绝错配
  • 客户端完全解耦:不依赖具体类,只依赖抽象
  • 扩展性极佳:新增 Oracle 产品族,只需新增 OracleFactory,无需改客户端
  • 符合开闭原则
⚠️ 缺点:
  • 每新增一个产品族,需新增一个工厂类
  • 代码量大,结构复杂

🔹 2. 抽象工厂模式 + Spring 容器(企业级推荐写法)

在 Spring 中,每个 Bean 就是一个产品,每个配置类就是一个工厂

/**
 * 【1】产品接口(同上)
 */
interface Connection {
    void connect();
}

interface Transaction {
    void begin();
    void commit();
    void rollback();
}

/**
 * 【2】具体产品:MySQL 实现
 */
@Component("mysqlConnection") // 注册为 Bean,名称为 mysqlConnection
class MySQLConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("🔌 MySQL: 成功连接到 MySQL 数据库");
    }
}

@Component("mysqlTransaction")
class MySQLTransaction implements Transaction {
    @Override
    public void begin() {
        System.out.println("🔄 MySQL: 开始事务");
    }

    @Override
    public void commit() {
        System.out.println("✅ MySQL: 提交事务");
    }

    @Override
    public void rollback() {
        System.out.println("❌ MySQL: 回滚事务");
    }
}

/**
 * 【3】具体产品:PostgreSQL 实现
 */
@Component("postgresqlConnection")
class PostgreSQLConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("🔌 PostgreSQL: 成功连接到 PostgreSQL 数据库");
    }
}

@Component("postgresqlTransaction")
class PostgreSQLTransaction implements Transaction {
    @Override
    public void begin() {
        System.out.println("🔄 PostgreSQL: 开始事务");
    }

    @Override
    public void commit() {
        System.out.println("✅ PostgreSQL: 提交事务");
    }

    @Override
    public void rollback() {
        System.out.println("❌ PostgreSQL: 回滚事务");
    }
}

/**
 * 【4】抽象工厂接口(可选,Spring 中可省略)
 */
interface DatabaseFactory {
    Connection getConnection();
    Transaction getTransaction();
}

/**
 * 【5】Spring 配置类:实现抽象工厂逻辑(推荐写法)
 */
@Configuration
public class DatabaseConfig {

    @Value("${database.type:mysql}") // 从 application.yml 读取
    private String dbType;

    // ✅ 工厂方法:根据配置返回对应产品族
    @Bean
    @Primary // 默认 Bean
    public Connection connection() {
        return switch (dbType.toLowerCase()) {
            case "mysql" -> applicationContext.getBean("mysqlConnection", Connection.class);
            case "postgresql" -> applicationContext.getBean("postgresqlConnection", Connection.class);
            default -> throw new IllegalArgumentException("不支持的数据库类型:" + dbType);
        };
    }

    @Bean
    public Transaction transaction() {
        return switch (dbType.toLowerCase()) {
            case "mysql" -> applicationContext.getBean("mysqlTransaction", Transaction.class);
            case "postgresql" -> applicationContext.getBean("postgresqlTransaction", Transaction.class);
            default -> throw new IllegalArgumentException("不支持的数据库类型:" + dbType);
        };
    }

    @Autowired
    private ApplicationContext applicationContext; // 注入 Spring 上下文
}

/**
 * 【6】服务层:使用产品族
 */
@Service
public class OrderService {

    @Autowired
    private Connection connection; // Spring 自动注入

    @Autowired
    private Transaction transaction; // Spring 自动注入

    public void processOrder() {
        System.out.println("📦 开始处理订单...");
        connection.connect(); // 连接数据库
        transaction.begin();  // 开始事务
        transaction.commit(); // 提交事务
        System.out.println("✅ 订单处理完成");
    }
}

/**
 * 【7】配置文件:application.yml
 */
/*
database:
  type: postgresql  # 切换为 mysql 或 postgresql,无需改代码!
*/

/**
 * 【8】启动类测试
 */
@SpringBootApplication
public class SpringAbstractFactoryDemo {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringAbstractFactoryDemo.class, args);
        OrderService service = context.getBean(OrderService.class);
        service.processOrder();

        // 输出(当 db.type=postgresql):
        // 📦 开始处理订单...
        // 🔌 PostgreSQL: 成功连接到 PostgreSQL 数据库
        // 🔄 PostgreSQL: 开始事务
        // ✅ PostgreSQL: 提交事务
        // ✅ 订单处理完成
    }
}
✅ 优点:
  • 零手写工厂类:Spring 自动管理 Bean 生命周期
  • 配置驱动application.yml 动态切换整个产品族
  • 支持 AOP、事务、缓存:所有 Spring 功能自动生效
  • 类型安全:编译期检查 Bean 类型
  • 真正企业级实现Spring Data JPA、MyBatis、RabbitMQ 都是这样实现的!
💡 Spring 的精髓

你不需要写 DatabaseFactory 接口,因为 Spring 容器本身就是抽象工厂!
@Bean 方法 = 抽象工厂的 createProductA()
@Value + @Conditional = 动态选择产品族


🔹 3. 抽象工厂模式 + 枚举 + 注解(轻量级高级写法)

适用于产品族固定、不频繁变更的场景,代码更简洁

/**
 * 【1】数据库类型枚举
 */
public enum DatabaseType {
    MYSQL, POSTGRESQL
}

/**
 * 【2】产品接口(同上)
 */
interface Connection {
    void connect();
}

interface Transaction {
    void begin();
    void commit();
    void rollback();
}

/**
 * 【3】具体产品
 */
class MySQLConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("🔌 MySQL: 连接成功");
    }
}

class MySQLTransaction implements Transaction {
    @Override
    public void begin() { System.out.println("🔄 MySQL: 开始事务"); }
    @Override
    public void commit() { System.out.println("✅ MySQL: 提交事务"); }
    @Override
    public void rollback() { System.out.println("❌ MySQL: 回滚事务"); }
}

class PostgreSQLConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("🔌 PostgreSQL: 连接成功");
    }
}

class PostgreSQLTransaction implements Transaction {
    @Override
    public void begin() { System.out.println("🔄 PostgreSQL: 开始事务"); }
    @Override
    public void commit() { System.out.println("✅ PostgreSQL: 提交事务"); }
    @Override
    public void rollback() { System.out.println("❌ PostgreSQL: 回滚事务"); }
}

/**
 * 【4】抽象工厂接口
 */
interface DatabaseFactory {
    Connection createConnection();
    Transaction createTransaction();
}

/**
 * 【5】枚举实现抽象工厂(每个枚举常量是一个工厂)
 */
public enum DatabaseFactoryProvider implements DatabaseFactory {
    MYSQL {
        @Override
        public Connection createConnection() {
            return new MySQLConnection();
        }

        @Override
        public Transaction createTransaction() {
            return new MySQLTransaction();
        }
    },
    POSTGRESQL {
        @Override
        public Connection createConnection() {
            return new PostgreSQLConnection();
        }

        @Override
        public Transaction createTransaction() {
            return new PostgreSQLTransaction();
        }
    };

    // 静态方法:根据枚举值获取工厂
    public static DatabaseFactoryProvider fromString(String type) {
        for (DatabaseFactoryProvider p : values()) {
            if (p.name().equalsIgnoreCase(type)) {
                return p;
            }
        }
        throw new IllegalArgumentException("未知数据库类型:" + type);
    }
}

/**
 * 【6】客户端
 */
public class EnumAbstractFactoryDemo {
    public static void main(String[] args) {
        String dbType = "MYSQL"; // 从配置读取

        DatabaseFactory factory = DatabaseFactoryProvider.fromString(dbType);

        Connection conn = factory.createConnection();
        Transaction tx = factory.createTransaction();

        conn.connect();
        tx.begin();
        tx.commit();
    }
}
✅ 优点:
  • 类型安全:枚举值有限,编译期检查
  • 无反射:性能高
  • 适合静态配置
⚠️ 缺点:
  • 不支持动态加载(如从数据库读取)
  • 不适合 Spring 管理的 Bean

✅ 六、抽象工厂模式的避坑指南(Java 后端高频踩坑)

问题原因解决方案
❌ 混淆工厂方法和抽象工厂误以为“创建多个对象”就是抽象工厂✅ 判断:是否创建配套产品族?是 → 抽象工厂
❌ 产品族不配套MySQL 连接 + Oracle 事务✅ 用抽象工厂强制配套,或用 Spring Bean 依赖注入
❌ 在抽象工厂中注入具体实现违反依赖倒置✅ 所有产品用接口,工厂只返回接口
❌ 忘记使用配置驱动手动写 if-else✅ 用 @Value + @Conditional 自动选择
❌ 用抽象工厂创建简单对象过度设计✅ 字段少于 3 个,用工厂方法或直接 new
❌ 没有单元测试难以验证产品族一致性✅ 编写测试验证 connectiontransaction 来自同一工厂

✅ 七、抽象工厂模式 vs 工厂方法模式 对比总结表

维度工厂方法模式抽象工厂模式
产品数量一个一组(产品族)
产品关系独立相互依赖、必须配套
客户端关注点“我要一个支付方式”“我要一套完整的数据库系统”
扩展难度新增一个产品新增一个产品族
典型实现PaymentFactory.create()DatabaseFactory.createConnection() + createTransaction()
Spring 对应@Bean 返回单个 Bean@Bean 返回一组协同的 Bean
推荐使用支付方式、日志输出数据库、缓存、消息队列、安全认证

终极口诀
“一个产品选工厂方法,一套系统选抽象工厂”


✅ 八、抽象工厂模式的典型实战案例:多租户 SaaS 系统

一个 SaaS 平台,不同租户使用不同缓存、不同日志、不同数据库

// 抽象产品族
interface Cache { void put(String key, Object value); }
interface Logger { void info(String msg); }
interface Database { void execute(String sql); }

// 产品族1:Redis 系统
class RedisCache implements Cache { public void put(String key, Object value) { System.out.println("🚀 Redis: 缓存 " + key); } }
class RedisLogger implements Logger { public void info(String msg) { System.out.println("📝 Redis 日志: " + msg); } }
class RedisDatabase implements Database { public void execute(String sql) { System.out.println("💾 Redis DB: " + sql); } }

// 产品族2:Memcached 系统
class MemcachedCache implements Cache { public void put(String key, Object value) { System.out.println("⚡ Memcached: 缓存 " + key); } }
class MemcachedLogger implements Logger { public void info(String msg) { System.out.println("📝 Memcached 日志: " + msg); } }
class MemcachedDatabase implements Database { public void execute(String sql) { System.out.println("💾 Memcached DB: " + sql); } }

// 抽象工厂
interface TenantFactory {
    Cache createCache();
    Logger createLogger();
    Database createDatabase();
}

// 具体工厂
class RedisTenantFactory implements TenantFactory {
    @Override public Cache createCache() { return new RedisCache(); }
    @Override public Logger createLogger() { return new RedisLogger(); }
    @Override public Database createDatabase() { return new RedisDatabase(); }
}

class MemcachedTenantFactory implements TenantFactory {
    @Override public Cache createCache() { return new MemcachedCache(); }
    @Override public Logger createLogger() { return new MemcachedLogger(); }
    @Override public Database createDatabase() { return new MemcachedDatabase(); }
}

// 服务层
@Service
public class TenantService {

    @Autowired
    private TenantFactory factory; // 由 Spring 根据租户 ID 自动注入

    public void processRequest() {
        Cache cache = factory.createCache();
        Logger logger = factory.createLogger();
        Database db = factory.createDatabase();

        cache.put("user:1001", "张三");
        logger.info("用户登录");
        db.execute("SELECT * FROM users WHERE id=1001");
    }
}

Spring 实现
@ConditionalOnProperty + @Profile + @Bean,根据租户 ID 动态注入不同工厂!


✅ 九、学习建议与进阶路径

阶段建议
📚 第一周用抽象工厂模式重构你项目中的“数据库 + 事务”模块(MySQL/PostgreSQL)
📚 第二周用 Spring @Bean + @Value 实现数据库类型动态切换
📚 第三周用抽象工厂模式实现“缓存系统”(Redis vs Caffeine)
📚 第四周阅读 Spring Boot 源码:DataSourceAutoConfiguration 如何实现抽象工厂?
📚 面试准备准备回答:

“你项目中哪里用了抽象工厂?”
“抽象工厂和工厂方法区别?”
“Spring 中如何实现抽象工厂?”
“多租户系统如何用抽象工厂?” |


✅ 十、抽象工厂模式面试高频题(附答案)

Q1:抽象工厂模式解决了什么问题?

A:解决了一次性创建一组相互依赖、风格一致的对象的问题,确保产品族一致性。

Q2:抽象工厂模式和工厂方法模式的区别?

A:

  • 工厂方法:创建一个对象(如支付方式)
  • 抽象工厂:创建一组对象(如数据库连接+事务+SQL解析器)
    → 一个管“单件”,一个管“套装”

Q3:Spring 中的 @Bean 是抽象工厂吗?

A:是的!@Bean 方法创建一组协同工作的 Bean,就是抽象工厂模式的体现。

Q4:什么时候不该用抽象工厂?

A:产品之间没有依赖关系,或只需创建一个对象,此时用工厂方法更简单。

Q5:抽象工厂模式如何实现热切换?

A:通过配置文件(如 application.yml)或数据库配置动态注入不同工厂实现,Spring 自动切换。


✅ 十一、总结:抽象工厂模式选型决策树

graph TD
    A[需要创建多个对象吗?] --> B{这些对象是否必须配套使用?}
    B -->|是| C{是否使用 Spring?}
    C -->|是| D[用 @Bean + @Value + @Conditional 自动注入产品族]
    C -->|否| E[用抽象工厂接口 + 具体工厂类]
    B -->|否| F[用工厂方法模式]

最终推荐

  • Spring 项目 → ✅ @Bean + @Value + @Conditional(最优雅)
  • 非 Spring、产品族固定 → ✅ 枚举 + 抽象工厂(最安全)
  • 非 Spring、需扩展 → ✅ 接口 + 具体工厂类(最标准)
  • 绝对不要:手动 new 多个对象、拼凑不配套产品

✅ 十二、结语:真正的高手,不写 new,而写“整套配置”

你不是在学“抽象工厂模式”,你是在学“如何设计一套可插拔、可切换、一致性的系统架构”。

当你看到 @Bean Connection@Bean Transaction 由同一个 @Value 控制时,你已经掌握了企业级架构的精髓。
当你用 application.yml 切换整个数据库系统,而不改一行代码时,你已经是架构师了。

不要为了“用模式”而用模式,
要为了“系统可配置、可扩展、可维护”而选择它。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值