设计模式之基于接口而非实现的设计原则

概念

基于接口而非实现的设计原则是一种重要的软件设计原则,它强调在设计和开发软件时,应该更多地关注接口而非具体的实现细节。这一原则有助于实现软件的可扩展性、可维护性和灵活性。

首先,基于接口的设计原则有助于实现软件的可扩展性。当软件需要与外部系统或组件进行交互时,通过定义明确的接口,可以使得软件能够更容易地集成新的系统或组件,而无需修改原有的代码。这样,当业务需求发生变化时,只需要调整接口的实现,而不需要对整个系统进行大规模的修改。

其次,基于接口的设计原则也有助于提高软件的可维护性。通过将实现细节隐藏在接口之后,可以降低软件系统的耦合度,使得各个组件之间的依赖关系更加清晰和简单。这样,当某个组件出现问题时,可以更容易地定位和解决问题,而不会影响到其他组件的正常运行。

此外,基于接口的设计原则还可以提高软件的灵活性。通过定义统一的接口标准,可以使得不同的系统或组件能够以一种一致的方式进行交互,从而实现跨平台、跨语言的互操作性。这种灵活性使得软件能够更好地适应不同的业务场景和需求变化。

在实际应用中,基于接口的设计原则可以通过多种方式来实现。例如,可以使用接口定义语言(IDL)来描述接口,从而使得不同的开发团队能够基于统一的接口标准进行开发。同时,也可以采用面向接口编程的编程范式,将实现与接口分离,使得代码更加清晰和易于维护。

总之,基于接口而非实现的设计原则是一种重要的软件设计思想,它有助于提高软件的可扩展性、可维护性和灵活性。在软件开发过程中,我们应该积极采用这一原则,从而设计出更加健壮、可靠和高效的软件系统。

例子

举一个基于Java的示例来说明基于接口而非实现的设计原则。在这个例子中,我们将创建一个简单的计算器应用,其中包含了加法和减法功能。我们将使用接口来定义计算器的行为,并通过实现这些接口来提供具体的计算逻辑。

首先,我们定义一个Calculator接口,它包含了addsubtract两个方法:

public interface Calculator {  
    int add(int a, int b);  
    int subtract(int a, int b);  
}

接下来,我们创建两个实现了Calculator接口的类:SimpleCalculatorAdvancedCalculatorSimpleCalculator提供了基本的加法和减法实现,而AdvancedCalculator可能包含更复杂的逻辑或额外的功能。

// SimpleCalculator类,实现了基本的加法和减法  
public class SimpleCalculator implements Calculator {  
    @Override  
    public int add(int a, int b) {  
        return a + b;  
    }  
  
    @Override  
    public int subtract(int a, int b) {  
        return a - b;  
    }  
}  
  
// AdvancedCalculator类,可能包含更复杂的计算逻辑或额外的功能  
public class AdvancedCalculator implements Calculator {  
    @Override  
    public int add(int a, int b) {  
        // 这里可以添加额外的逻辑,比如日志记录、错误处理等  
        return super.add(a, b); // 假设AdvancedCalculator扩展自另一个实现了Calculator的类  
    }  
  
    @Override  
    public int subtract(int a, int b) {  
        // 类似地,这里也可以添加额外的逻辑  
        return super.subtract(a, b);  
    }  
  
    // AdvancedCalculator可能还包含其他方法或功能  
}

现在,在客户端代码中,我们可以基于Calculator接口来操作计算器,而无需关心具体的实现细节。这允许我们在不修改客户端代码的情况下,轻松地替换或扩展计算器的实现。

public class CalculatorApp {  
    public static void main(String[] args) {  
        // 使用SimpleCalculator作为实现  
        Calculator calculator = new SimpleCalculator();  
        int sum = calculator.add(5, 3);  
        int difference = calculator.subtract(sum, 2);  
        System.out.println("Sum: " + sum);  
        System.out.println("Difference: " + difference);  
  
        // 假设我们想要使用AdvancedCalculator,只需要更改实现类的实例化即可  
        // calculator = new AdvancedCalculator();  
        // ... 执行相同的操作,但现在使用的是AdvancedCalculator的逻辑 ...  
    }  
}

在上面的代码中,CalculatorApp类依赖于Calculator接口而不是具体的实现类。因此,我们可以轻松地切换实现,而无需修改CalculatorApp中的代码。这种基于接口的设计原则使得代码更加灵活、可维护和可扩展。

优化

但是直接在代码中修改实例化的类可能不够灵活。一个更灵活的实现方式是使用工厂模式或依赖注入(Dependency Injection)来动态地创建和注入实现类的实例。这样,你就可以在运行时或配置文件中指定使用哪个实现,而无需修改客户端代码。

以下是使用依赖注入框架(例如Spring)的示例,它允许你在配置文件中定义实现类的选择,并在运行时自动注入到需要的地方。

首先,定义你的接口和实现类,这部分与之前的例子相同:

public interface Calculator {  
    int add(int a, int b);  
    int subtract(int a, int b);  
}  
  
public class SimpleCalculator implements Calculator {  
    // ... 实现细节 ...  
}  
  
public class AdvancedCalculator implements Calculator {  
    // ... 实现细节 ...  
}

然后,在你的应用程序中,你不再直接实例化Calculator的实现类。相反,你声明一个Calculator类型的字段,并依赖外部机制(如Spring容器)来注入正确的实现。

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  
  
@Component  
public class CalculatorApp {  
    private final Calculator calculator;  
  
    @Autowired  
    public CalculatorApp(Calculator calculator) {  
        this.calculator = calculator;  
    }  
  
    public void performCalculations() {  
        int sum = calculator.add(5, 3);  
        int difference = calculator.subtract(sum, 2);  
        System.out.println("Sum: " + sum);  
        System.out.println("Difference: " + difference);  
    }  
}

在Spring的配置文件或Java配置中,你可以指定Calculator接口的实现类:

import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
  
@Configuration  
public class AppConfig {  
    @Bean  
    public Calculator calculator() {  
        // 这里返回你想要的实现类实例  
        // 可以通过条件注解或其他逻辑来动态决定返回哪个实现  
        return new SimpleCalculator(); // 或者返回 new AdvancedCalculator();  
    }  
}

现在,当你运行你的应用程序时,Spring容器会自动管理Calculator接口的实例,并将其注入到CalculatorApp类中。你可以通过修改配置文件或Java配置来轻松地切换实现,而无需修改CalculatorApp类的代码。

这种方式的优点是它提供了更高的灵活性和可配置性。你可以根据不同的环境、需求或配置来选择不同的实现,而无需修改应用程序的核心逻辑。同时,它也促进了代码的松耦合,使得各个组件更加独立和可重用。

 

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 第1章 绪论 1 1.1 引言 1 第2章 概述 3 2.1系统总概述 3 2.2总体方案设计 3 2.3硬件设计 3 2.4软件设计 4 2.5 硬件结构框图2.1 4 2.6 软件结构框图2.2 4 第3章 硬件设计 5 3.1 硬件设计主电路图见附录9.1 5 3.2 硬件选择 5 3.2.1 MCU的选择与简介 5 3.2.2 单片机最小系统的实现 10 3.2.3 数据采集系统 13 3.2.4 模数转换的选择与简介 14 3.2.5 按键选择与简介 16 3.2.6 外围扩充存储器 17 3.2.7 时钟芯片选择与简介 18 3.2.8 上拉电阻 20 3.2.9 液晶显示器简介。 20 3.2.10 报警电路 23 3.2.11 硬件仿真环境介绍 24 第4章 软件设计 26 4.1编写语言的选择 26 4.2 编译软件介绍 27 4.3主程序模块 32 4.4模数转换 33 4.5按键模块 33 4.6时钟模块 34 4.7液晶显示模块 34 第5章 系统仿真 35 第6章 结束语 37 致 谢 38 参考文献 39 附录 40 第1章 绪论 1.1 引言 1.1.1甲醛的特性及危害 甲醛是一种无色,有强烈刺激性气味的气体。易溶于水、醇和醚。甲醛在常温下是气 态,通常以水溶液形式出现。其37%的水溶液称为福尔马林,医学和科研部门常用于标本 的防腐保存。此溶液沸点为19.5 故在室温时极易挥发,随着温度的上升甲醛的挥发速度 加快。在我国有毒化学品优先控制名单中甲醛列居第二位。 1.1.2甲醛的来源 1.室内装修所用的合成板材,如胶合板、细木工板、高密度板、刨花板。这些板材中 甲醛起胶合剂、防腐剂的作用,主要用于加强板材的硬度、防虫、防腐。板材中残留的 和未参与反应的甲醛逐渐向周围环境释放,是室内空气中甲醛的主要来源。 2.用合成板材制造的家具,厂家为了追求利润使用不合格的板材,再粘贴面材料时使 用不合格的胶水,造成家具中甲醛含量超标。 3.含有甲醛成分并有可能向外界散发的各类装饰材料,如壁纸、地毯、油漆。 第2章 概述 2.1系统总概述 本论文主要完成甲醛检测仪软件设计,设计内容包括:A/D转换器程序、控制程序、 超标报警、键盘检测、数据显示等。 本系统采用单片机为控制核心,以实现便携式甲醛检测仪的基本控制功能。系统主要 功能内容包括:数据处理、时间设置、开始测量、超标报警、键盘检测、自动休眠:仪 器若不进行称量操作,5分钟后自动进入休眠模式,以降低电源消耗。 本系统设计采用功能模块化的设计思想,系统主要分为总体方案设计、硬件和软件的 设计三大部分。 2.2总体方案设计 室内甲醛污染对人身体健康影响较大,标准规定的方法绝大多数是化学分析法,使用 的手段是实验室分析仪器 主要有比色计、 分光光度计、化学滴定、 气相和液相色谱。但这些方法费力费时、成本高、自动化程度低过程复杂、大多数过程 是人工操作很难做到现场实时控制随着传感器和计算机技术的不断发展,现已有了基于 单片机的便携式甲醛测试仪,并且测试测试范围、分辨率、精度、稳定性已接近标准要 求。因此本设计可选用基于电化学原理的甲醛传感器,其原理是空气中的甲醛在电极下 发生氧化反应,产生的扩散电极电流与空气中的甲醛浓度成正比,通过检测放大电路和 放大倍数的调整经A/D转换后送单片机 、由单片机现场自动控制检测并显示甲醛浓度。由于甲醛含量超量的话,将对人体健康 造成很大的影响。具有民用价值的便携式甲醛检测仪的研制受到了人们的高度重视。设 计能够满足生活需要,携带方便的便携式甲醛检测仪迫在眉睫。针对目前的现状,本系 统设计遵守体积小,质量轻,性价比高的原则。 2.3硬件设计 硬件设计部分主要包括:(MCU、A/D、时钟芯片、LCD、外围扩展数据RAM)等芯片的 选择; 硬件主电路设计、数据采集、模数转换电路设计、液晶显示电路设计、外围扩充存储器 接口电路、时钟电路、复位电路、键盘接口电路等功能模块电路设计。 2.4软件设计 软件设计部分主要包括:编写语言的选择、主程序/子程序流程的设计、功能模块程 序的编写、软/硬件结合调试与演示。主要包括一下功能模块:51驱动、检测、液晶显示 、时钟、键盘、模数软换。 2.5 硬件结构框图2.1 图2.1 2.6 软件结构框图2.2 图2.2 第3章 硬件设计 3.1 硬件设计主电路图见附录9.1 3.2 硬件选择 3.2.1 MCU的选择与简介 1. 单片机的概念和特点 现代社会中,尽管PC机的应用已经相当普遍,但是,在工控领域,在日益追求小而精、 轻而薄的自动化控制器、自动化仪器仪表、家电产品等方面,PC机仍有所不相适宜的地方 。而工业控制、仪器仪表、家电产品等市场广阔,要求PC机技术与之相适应。在这种情 况下,单片机应运而生
资源介绍 本资源是一个基于Spring Boot开发的图书商城管理系统的完整毕业设计项目,包含了毕业论文和源代码,是计算机科学或软件工程相关专业学生进行毕业设计的理想选择。该系统旨在实现一个功能完备的图书商城,涵盖了图书展示、购物车管理、订单处理、用户认证与权限管理等多个核心模块,为学生提供了一个实战演练的机会,使其能够深入理解并掌握Spring Boot框架的开发流程和技术要点。 在毕业论文部分,详细阐述了项目的背景、需求分析、系统设计、实现过程以及测试验证等各个环节,不仅有助于学生系统地梳理项目开发的思路和方法,还能为他们的论文撰写提供有力的支撑和参考。 源代码部分则采用了模块化、高内聚低耦合的设计原则,使得代码结构清晰、易于维护。通过使用Spring Boot框架,项目能够快速搭建并集成各种所需的功能组件,提高了开发效率。同时,系统还采用了前后端分离的开发模式,前端使用流行的前端框架进行开发,后端则提供RESTful API接口供前端调用,实现了前后端的解耦,便于后续的维护和扩展。 此外,该系统还具有良好的可扩展性和可定制性,学生可以根据自己的需求对系统进行二次开发,添加更多的功能模块或优化现有功能,以满足实际业务场景的需求。 总的来说,本资源不仅为学生提供了一个完整的毕业设计项目示例,还为他们提供了一个学习和实践Spring Boot框架的绝佳平台,有助于他们提升软件开发能力和实战经验。
第一部分 背景知识 第1章 应重视的价值,也是对过去几年的沉重反思 1.1 总体价值 1.2 应重视的架构风格 1.2.1 焦点之一:模型 1.2.2 焦点之二:用例 1.2.3 如果重视模型,就可以使用领域模型模式 1.2.4 慎重处理数据库 1.2.5 领域模型与关系数据库之间的阻抗失配 1.2.6 谨慎处理分布式 1.2.7 消息传递很重要 1.3 对过程的各个组成部分的评价 1.3.1 预先架构设计 1.3.2 领域驱动设计 1.3.3 测试驱动开发 1.3.4 重构 1.3.5 选择一种还是选择组合 1.4 持续集成 1.4.1 解决方案(或至少是正确方向上的一大步) 1.4.2 从我的组织汲取的教训 1.4.3 更多信息 1.5 不要忘记运行机制 1.5.1 有关何时需要运行机制的一个例子 1.5.2 运行机制的一些例子 1.5.3 它不仅仅是我们的过错 1.6 小结 第2章 模式起步 2.1 模式概述 2.1.1 为什么要学习模式 2.1.2 在模式方面要注意哪些事情 2.2 设计模式 2.3 架构模式 2.3.1 示例:层 2.3.2 另一个示例:领域模型模式 2.4 针对具体应用程序类型的设计模式 2.5 领域模式 2.6 小结 第3章 TDD与重构 3.1 TDD 3.1.1 TDD流程 3.1.2 演示 3.1.3 设计效果 3.1.4 问题 3.1.5 下一个阶段 3.2 模拟和桩 3.2.1 典型单元测试 3.2.2 声明独立性 3.2.3 处理困难因素 3.2.4 用测试桩替换协作对象 3.2.5 用模拟对象替换协作对象 3.2.6 设计含义 3.2.7 结论 3.2.8 更多信息 3.3 重构 3.4 小结 第二部分 应用DDD 第4章 新的默认架构 4.1 新的默认架构的基础知识 4.1.1 从以数据库为中心过渡到以领域模型为中心 4.1.2 进一步关注DDD 4.1.3 根据DDD进行分层 4.2 轮廓 4.2.1 领域模型示例的问题/特性 4.2.2 逐个处理特性 4.2.3 到目前为止的领域模型 4.3 初次尝试将UI与领域模型挂接 4.3.1 基本目标 4.3.2 简单UI的当前焦点 4.3.3 为客户列出订单 4.3.4 添加订单 4.3.5 刚才我们看到了什么 4.4 另一个维度 4.4.1 领域模型的位置 4.4.2 孤立或共享的实例 4.4.3 有状态或无状态领域模型实例化 4.4.4 领域模型的完整实例化或子集实例化 4.5 小结 第5章 领域驱动设计进阶 5.1 通过简单的TDD实验来精化领域模型 5.1.1 从Order和OrderFactory的创建开始 5.1.2 一些领域逻辑 5.1.3 第二个任务:OrderRepository+OrderNumber 5.1.4 重建持久化的实体:如何从外部设置值 5.1.5 获取订单列表 5.1.6 该到讨论实体的时候了 5.1.7 再次回到流程上来 5.1.8 总览图 5.1.9 建立OrderRepository的伪实现 5.1.10 简单讨论一下保存 5.1.11 每个订单的总量 5.1.12 历史客户信息 5.1.13 实例的生命周期 5.1.14 订单类型 5.1.15 订单的介绍人 5.2 连贯接口 5.3 小结 第6章 准备基础架构 6.1 将POCO作为工作方式 6.1.1 实体和值对象的PI 6.1.2 是否使用PI 6.1.3 运行时与编译时PI 6.1.4 PI实体/值对象的代价 6.1.5 将PI用于存储库 6.1.6 单组存储库的代价 6.2 对保存场景的处理 6.3 建立伪版本机制 6.3.1 伪版本机制的更多特性 6.3.2 伪版本的实现 6.3.3 影响单元测试 6.4 数据库测试 6.4.1 在每次测试之前重置数据库 6.4.2 在测试运行期间保持数据库的状态 6.4.3 测试之前重置测试所使用的数据 6.4.4 不要忘记不断演变的模式 6.4.5 分离单元测试和数据库调用测试 6.5 查询 6.5.1 单组查询对象 6.5.2 单组查询对象的代价 6.5.3 将查询定位到哪里 6.5.4 再次将聚合作为工具 6.5.5 将规格用于查询 6.5.6 其他查询选择 6.6 小结 第7章 应用规则 7.1 规则的分类 7.2 规则的原则及用法 7.2.1 双向规则检查:可选的(可能的)主动检查,必需的(和自动的)被动检查 7.2.2 所有状态(即使是错误状态)都应该是可保存的 7.2.3 规则应该高效使用 7.2.4 规则应该是可配置的,以便添加自定义规则 7.2.5 规则应与状态放在一起 7.2.6 规则应该具有很高的可测试性 7.2.7 系统应阻止我们进入错的状态 7.3 开始创建API 7.3.1 上下文,上下文,还是上下文 7.3.2 数据库约束 7.3.3 将规则绑定到与领域有关的转换,还是绑定到与基础架构有关的转换 7.3.4 精化原则:所有状态,即使是错误状态,都应该是可保存的 7.4 与持久化有关的基本的规则API的需求 7.4.1 回到已发现的API问题上 7.4.2 问题是什么 7.4.3 我们允许了不正确的转换 7.4.4 如果忘记检查怎么办 7.5 关注与领域有关的规则 7.5.1 需要合作的规则 7.5.2 使用基于集合的处理方法 7.5.3 基于服务的验证 7.5.4 在不应该转换时尝试转换 7.5.5 业务ID 7.5.6 避免问题 7.5.7 再次将聚合作为工具 7.6 扩展API 7.6.1 查询用于设置UI的规则 7.6.2 使注入规则成为可能 7.7 对实现进行精化 7.7.1 一个初步实现 7.7.2 创建规则类,离开最不成熟的阶段 7.7.3 设置规则列表 7.7.4 使用规则列表 7.7.5 处理子列表 7.7.6 一个API改进 7.7.7 自定义 7.7.8 为使用者提供元数据 7.7.9 是否适合用模式来解决此问题 7.7.10 复杂规则又是什么情况 7.8 绑定到持久化抽象 7.8.1 使验证接口成为可插入的 7.8.2 在保存方面实现被动验证的替代解决方案 7.8.3 重用映射元数据 7.9 使用泛型和匿名方法 7.10 其他人都做了什么 7.11 小结 第三部分 应用PoEAA 第8章 用于持久化的基础架构 8.1 持久化基础架构的需求 8.2 将数据存储到哪里 8.2.1 RAM 8.2.2 文件系统 8.2.3 对象数据库 8.2.4 关系数据库 8.2.5 使用一个还是多个资源管理器 8.2.6 其他因素 8.2.7 选择和前进 8.3 方法 8.3.1 自定义手工编码 8.3.2 自定义代码的代码生成 8.3.3 元数据映射(对象关系(O/R)映射工具) 8.3.4 再次选择 8.4 分类 8.4.1 领域模型风格 8.4.2 映射工具风格 8.4.3 起点 8.4.4 API焦点 8.4.5 查询风格 8.4.6 高级数据库支持 8.4.7 其他功能 8.5 另一个分类:基础架构模式 8.5.1 元数据映射:元数据的类型 8.5.2 标识字段 8.5.3 外键映射 8.5.4 嵌入值 8.5.5 继承解决方案 8.5.6 标识映射 8.5.7 操作单元 8.5.8 延迟加载/立即加载 8.5.9 并发控制 8.6 小结 第9章 应用NHibernate 9.1 为什么使用NHibernate 9.2 NHibernate简介 9.2.1 准备 9.2.2 一些映射元数据 9.2.3 一个小的API示例 9.2.4 事务 9.3 持久化基础架构的需求 9.3.1 高级持久化透明 9.3.2 持久化实体的生命周期所需的特定特性 9.3.3 谨慎处理关系数据库 9.4 分类 9.4.1 领域模型风格 9.4.2 映射工具风格 9.4.3 起点 9.4.4 API焦点 9.4.5 查询语言风格 9.4.6 高级数据库支持 9.4.7 其他功能 9.5 另一种分类:基础架构模式 9.5.1 元数据映射:元数据类型 9.5.2 标识字段 9.5.3 外键映射 9.5.4 嵌入值 9.5.5 继承解决方案 9.5.6 标识映射 9.5.7 操作单元 9.5.8 延迟加载/立即加载 9.5.9 并发性控制 9.5.10 额外功能:验证挂钩 9.6 NHibernate和DDD 9.6.1 程序集概览 9.6.2 ISession和存储库 9.6.3 ISession、存储库和事务 9.6.4 得到了什么结果 9.7 小结 第四部分 下一步骤 第10章 博采其他设计技术 10.1 上下文为王 10.1.1 层和分区 10.1.2 分区的原因 10.1.3 限界上下文 10.1.4 限界上下文与分区有何关联 10.1.5 向上扩展DDD项目 10.1.6 为什么对领域模型——SO分区 10.2 SOA简介 10.2.1 什么是SOA 10.2.2 为什么需要SOA 10.2.3 SOA有什么不同 10.2.4 什么是服务 10.2.5 服务中包括什么 10.2.6 深入分析4条原则 10.2.7 再来看一下什么是服务 10.2.8 OO在SOA中的定位 10.2.9 客户-服务器和SOA 10.2.10 单向异步消息传递 10.2.11 SOA如何提高可伸缩性 10.2.12 SOA服务的设计 10.2.13 服务之间如何交互 10.2.14 SOA和不可用的服务 10.2.15 复杂的消息传递处理 10.2.16 服务的可伸缩性 10.2.17 小结 10.3 控制反转和依赖注入 10.3.1 任何对象都不是孤岛 10.3.2 工厂、注册类和服务定位器 10.3.3 构造方法依赖注入 10.3.4 setter依赖注入 10.3.5 控制反转 10.3.6 使用了Spring.NET框架的依赖注入 10.3.7 利用PicoContainer.NET进行自动装配 10.3.8 嵌套容器 10.3.9 服务定位器与依赖注入的比较 10.3.10 小结 10.4 面向方面编程 10.4.1 热门话题有哪些 10.4.2 AOP术语定义 10.4.3 .NET中的AOP 10.4.4 小结 10.5 小结 第11章 关注UI 11.1 提前结语 11.2 模型-视图-控制器模式 11.2.1 示例:Joe的Shoe Shop程序 11.2.2 通过适配器简化视图界面 11.2.3 将控制器从视图解耦 11.2.4 将视图和控制器结合起来 11.2.5 是否值得使用MVC 11.3 测试驱动的Web窗体 11.3.1 背景 11.3.2 一个示例 11.3.3 领域模型 11.3.4 GUI的TDD 11.3.5 Web窗体实现 11.3.6 小结 11.3.7 用NMock创建模拟 11.4 映射和包装 11.4.1 映射和包装 11.4.2 用表示模型来包装领域模型 11.4.3 将表示模型映射到领域模型 11.4.4 管理关系 11.4.5 状态问题 11.4.6 最后的想法 11.5 小结 11.6 结束语 第五部分 附录 附录A 其他领域模型风格 附录B 已讨论的模式的目录

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值