refactoring Patterns第六部分

DexProtector内存校验绕过与DEX提取实战 DEX文件是Android应用的核心执行单元,其动态加载与运行时保护机制直接关系到App安全强度。DexProtector作为主流商业加固方案,摒弃传统文件级防护,转而依托ART虚拟机下的匿名内存映射段(anonymous mmap)构建多维校验体系——涵盖字节码完整性校验、内存页属性检测及运行时环境感知。其技术价值在于将防护锚点从静态APK迁移至动态内存态,大幅提升逆向门槛。典型应用场景包括金融类App的防篡改加固、游戏客户端的反外挂保护,以及政企应用的合规性安全增强。本文聚焦DexProtector v 阅读详情
Refactoring是一种重要的设计辅助工具。特别地,他能够使得传统的up-front设计更简单,也可以改良现有软件的设计。本文阐述了在应用refactoring环境下设计应当具有的特点以及refactoring和OO社团最受人瞩目之一的设计模式之间的关系。

简化设计

为什么设计应当是简单的?
传统的软件方法偏向于进行一次性的Upfront设计,我们知道这很难。

软件方法学的设计者通常喜欢用建筑打比方。他们说,如果你要建造一座大厦,那么在你画完所有的施工图和建筑规范之前,你从来不应该开始真正施工。

我喜欢这样的比喻,因为建筑学的发展经历了几千年甚至可能是上万年,而软件行业,最多也只有5,60年的时间,我们需要从建筑中学习很多东西。就如模式语言的提出者,著名的建筑学家,Christopher Alexander ,给了设计模式很大的启发。

但是,如果我们只是从建筑学到东西,恐怕还是远远不够。我还不知道哪一栋大厦的外形能够发生变化,或者哪一个大商场在发现自动扶梯位置不太合适时,能够自由移动到一个更加合适的地方(我附近一家大超市的自动扶梯真的让我想到了这一点)。也许要部分达到这样的目的还是有可能的,但肯定要花费极高的代价。但是如果我们去构造一个企业的应用系统,那么我们必须准备好企业发展的同时需要对我们的软件做出改变。

软件的核心就是可变。这种变化不仅仅在于需求的变化,还在于人们理解的变化,而软件这种人活动的产物,必须要随之发生改变。

增量迭代的开发方法学是对软件变化的一种响应。但是正如我们前面所讲,增量迭代模型有可能对系统的设计提出比传统的Waterfall更复杂的要求。你需要预期所有可能的情形,然后才能保证你是可以增量的,也是能够迭代的。

这种两难的境地来源于一个基本的假设:
削减软件成本的最主要方法是减少返工的成本以及它的可能性。

传统的软件发法学认为,如果我能够尽量早地冻结需求,那么我修改设计的可能性和成本就越小,如果我的设计能够尽早地冻结,那么我修改实现的可能性和成本就越小。要想早早冻结设计,它必须是非常详尽而复杂的。

我把这种思路称为"改变恐惧症",不仅仅因为变化是最难研究的东西(制订出条条框框则简单的多),也因为我们没有足够的经验来适应变化,更重要的是,从来没有哪个时代比今天变化更快。

现在我们从另一个角度来想问题:
我们是否可能通过增加返工的方法来削减软件的成本?

这个方法初看起来自相矛盾。但是Kent Beck说:
The key is that risk is money just as much as time is money。

既然系统在变,那么你不能肯定今天设计的东西一定能够被日后用到。而你需要等待设计完成的成本会很高,因为在你的设计完成之前,没有人可以开始工作。

设计不是独立的。别人要使用你的设计,他必须能够理解你的设计。如果你今天的设计十分复杂,那么你就增加了从今天开始的所有开销,更多的东西需要检查和测试,更多的东西需要理解,更多的东西需要解释。更重要的是,你不能估算出明日的成本。你必须估计日后将要发生什么事情,通常你不可能准确无误地做到这一点。

所以,让设计简单,我们可以通过不断地增量修改设计使得系统更加接近需求,就象我们开车,每次都做一些小小的调整,最后达到目的地。

什么才是简单的设计?
要让设计简单,是否意味着我们可以随意fix and build,写一段,然后任意修改它。但是简单并不意味着随意,当然更不意味着愚蠢。

就象我在Duplicate Code里指出的一样,Once and Only Once通常是代码最简单的形式,因为它没有重复,每一个类都有自己简单的责任,每一个方法都有自己简单的意图。

简单的设计最重要的特性就是容易适应变化.为了达到这样的目的,简单设计应当:

  1. 能够简单地被理解.这依赖于你代码的可理解性.只有可理解,下面的简单性才能达到.
  2. 能够简单地被修改和扩展.OO系统往往通过增量的方法改变或增加系统行为,所以也就是要被简单地重用,这要求我们没有重复代码.
  3. 有最少数目的类.要易于理解系统,那么系统中每一个类应当和需要解决的问题的每一个重要概念相对应,如果人工加入许多毫无意义的类或者太多与问题概念无法对应的类,系统将无法理解
  4. 有最少数目的方法.每一个方法应该有他独立的意义, 如果没有可以言述意图的名字,那么系统将难以理解.


如果你看过Kent Beck和其他Agile联盟作者的文章,或者你看过我的Duplicate Code和Long Method,你可能会觉得2和3,4存在着矛盾.因为要达到2的目的,你必须有很多小类和精干的方法,而3,4则要求你具有最少的类和方法.要理解它们之间其实一致的关键在于,你如何决定需要一个新类或者需要一个新的方法.在进行Refactoring时,如果你觉得系统的某一部分具有独立的可对应问题领域的概念时,那么你应当毫不犹豫地使用Extract Class形成一个新类.反之,某一个类无法具有其独立的应用意义,或者该类数据太多而没有行为,这个时候你应当考虑这个类是不是应该被删除.当然,这里面有些例外(如method object)同样道理,如果一个代码片断能够有独立意图的行为,那么不管它的大小,可能是一个简单的表达式,都应该有独立的方法,但如果没有这样清晰的意图,再多的代码都可以在一个方法里面.

显然,简单的设计并非我们想象的那么简单.除了我们可以不太考虑以后解决的问题外,简单,作为一种重要审美标准.不是轻易能够达到的.我们一开始的设计往往不能够达到这样的要求,可能是类的划分不合理,可能存在着重复的代码,可能行为的分配需要调整,要达到简单,你就必须Refactoring你的代码,使设计更加合理.

Refactoring如何支持简单的upfront设计?
开始,你可以作简单的设计,也许是一个类,2、3个方法,你针对这个类写出test case,实现代码,让test case通过。在实现这个类的过程中,你发现2、3个方法太长,它们之间会有很多重复代码,于是你进行refactoring,你使用extract method让方法更能揭示意图。然后,你解决另外一个问题,你写出另外一个Test case,可能和上面一个类没有多大的关系,也可能你发现这个类在以前的类上附加了一些功能。这时,你会发现直接对前面一个类进行修改比较困难,你对前面一个类进行refactoring,让它更加容易加入。如果随着新功能的加入,发现前面一个类其实包含了两个应用概念,你可能需要对这个类进行重构,把它拆分为两个类。你进行refactoring,让所有的Test cast都能通过测试。你接着又选取一个需求,增加新的test case,然后实现它。这时候你发现这个类和原来的某个类具有很多类似的地方,你可能需要把这两个类进行进一步的抽象,通过refactoring形成一个新的超类。。。

随着时间的推移,越来越多的需求被实现,系统变得越来越大,某一天,你发觉系统有些变样,你觉得有必要修改系统的某一部分结构。这时候,你和你的同僚可能需要暂时摘下增加功能的帽子。你们可能需要更多的讨论,交流,使用任何有意义的方法为你们的讨论增强交流的效果,白板、CRC,然后进行Refactoring。

Kent Beck指出,某些大的Refactoring可能并不是一天就能完成的。它可能要花费几天甚至一个月的时间。但是,你还需要继续完成用户的需求。这时采取的方法就是小步的增量改变。在实现新需求,写下新的test case时,你可能看到一个机会能够让你的代码朝着大目标前进一步。使用这样的方法,每一次你的工作可能是移动一个变量或一个方法。但是随着新功能进一步的加入,这样的机会不断出现。最终,一个大的目标会变成很小的工作。这时候,你已经水到渠成,花上几分钟就完成了。

Refactoring的这种工作方式可以大大减少UpFront的设计量,它同时使你的设计变为一种必要和需求的产物,这种为了更好地加入新需求所做的设计,更准确地反映了问题的本身。同时,它使得设计随着你对问题的进一步深入而逐渐变得更合理,随着你对新技术的掌握而变得聪明,这是一种进化的设计方法。

设计模式

简单设计的要求
Refactoring对简单设计的支持好像使设计模式变得有些过时。如果我一开始只需要写下一个简单的test cast、实现、refactoring,增加test cast、实现、refactoring。这是否意味着设计模式就毫无意义了呢?

对设计模式和Refactoring,以及与之相关的Agile联盟方法学的研究给我一个很深刻的印象。一个方面,就像Martin Fowler指出,Agile方法学的提倡者往往也是模式社团的领导者。另一方面,如果你仔细体会一下Agile 宣言,你会发觉它们非常重视人的能力。在我自己组织小组试用XP的过程中也发现,XP对它的参与者有很高的要求。

我们已经看到,简单的设计并不是愚蠢的设计,相反,它们是极端聪明的设计。要实现简单的设计,你必须有丰富的知识。这里在于,简单设计提倡你不要太多考虑以后的具体需求本身,但它确实需要你考虑代码和设计的结构。在用Refactoring武装你的思想中,我已经讲到,如果你的设计结构完全不考虑以后的扩展,那么Refactoring也无能为力。

设计模式是面向对象社团对设计的一个重要总结。其实,它们也是对简单设计的最好诠释。因为,好的设计模式,就是对于解决问题所能发现的最简单、最易重用的设计结构。另一方面,使用广为人知的设计模式也使得你的代码更容易为人理解、接受,大大提高了交流的效率、宽度和深度。

从这种意义上将,Refactoring不但没有排除设计模式,它更促进了设计模式的学习、发现和验证,以及更广泛的应用。Agile对程序员素质的要求,在这一点上也得到了充分的体现。一旦小组成员具备熟练运用设计模式的能力,你的设计可能就是一句话:用XXX模式。(在我领导的EOSP2P项目中,这个模式就是半State/半策略模式。当我说出这个模式时,我们发觉整个小组不再需要upfront设计,我们构建,然后Refactoring。当然,象Factory method模式几乎使每个系统都应当使用的。还有后续的proxy模式,都让设计变得如此轻松)。

但是,在应用设计模式的过程中,你必须时时记住简单性这个原则。设计模式最好的应用方法就是从简单开始,只要能够解决问题,抓住这种模式的核心,那么模式良好的结构保证你的Refactoring建立在一个坚实的基础之上。

所以,为了使Refactoring能够更好地进行,你需要更多的学习模式,因为设计模式不但是良好设计的开始,同时也是Refactoring的目标。

Refactoring的目标
Refactoring有时会失去目标,你可能会产生循环的Refactoring,或者你无法判断Refactoring的结果到底有没有让程序的结构更好。

作为OO社团最重要贡献之一的设计模式能够为Refactoring提供一个明确的目标。在Ralph Johnson多篇关于Refactoring的论文中都提到了这个问题。Martin Fowler的《Refactoring》中明确指出的就有state、strategy、visitor等模式。更深入一步,设计模式不但是Refactoring的目标,同时也为看起来有些零散的行为提供了一个全局性的指引。

Christopher Alexander在《The Timeless way of Building》一书中对模式的定义如下:

Each pattern is a three-part rule, which expression a relation between a certain context, a problem, and a solution.

很多人在软件领域对他的定义做了修改和扩展,但这三部分内容是不变的。把这个定义放到我们软件上下文来看,设计模式有两个重要特性:

  1. 设计模式高于代码层,他不是描述一个好的编码风格,或者某种编程习惯用语;
  2. 设计模式不是纯粹理论上的体系结构或者分析方法,它是一种可实际操作的东西。模式界提出的Three Rules就说,你必须提出三个实际的应用例子才能够得上设计模式的条件。


正因如此,很多人都把设计模式称之为micro-architecture。显然,由于它的可操作性,它可以通过Refactoring的方法达到。同时由于它一定层次的理论和总结性,他可以用于引导Refactoring并作为Refactoring的目标。

 

 

 

关于作者
石一楹,现任浙江大学灵峰科技开发公司技术总监。多年从事OO系统分析、设计。现主要研究方向为Refactoring和分析模式。

操作系统实验四 进程运行轨迹的跟踪与统计(哈工大李治军) 实验4 进程运行轨迹的跟踪与统计 实验目的 掌握 Linux 下的多进程编程技术; 通过对进程运行轨迹的跟踪来形象化进程的概念; 在进程运行轨迹跟踪的基础上进行相应的数据统计,从而能对进程调度算法进行实际的量化评价,更进一步加深对调度和调度算法的理解,获得能在实际操作系统上对调度算法进行实验数据对比的直接经验。 实验内容 进程从创建(Linux 下调用 fork())到结束的整个过程就是进程的生命期,进程在其生命期中的运行轨迹实际上就表现为进程状态的多次切换,如进程创建以后会成为就绪态;当该进程被调度以 阅读详情

相关推荐

NX二次开发编译时dll自动数字签名及拷贝

在UG5.0开始,所有基于UG二次开发的DLL都要“签名”后才能被客户端上正版的NX调用。

菜鸟爱飞不飞的博客 2683

真专家其实是热情、厚道的

每次回杭州总能有很多收获,这次尤甚,感谢博文视点周筠老师在会议开始的前几天提供会议信息,使我得以成行。 见到“真正的技术大会” 参加过很多技术大会,但大都是由厂商主导,比如IBM的DW大会,MS的TechED,BEA的eWorld等,并不是说这些会议不好,而是说其内容难免会染上太多厂商的味道,失却些中立性。所以阿里巴巴这次能安排这么大、非盈利的大会,确实让人振奋。怀着一种美好的愿望来揣测马

编辑空间 2905

Python实现支付宝转账接口

由于工作需要使用python开发一个自动转账接口,记录一下开发过程。 首先需要在蚂蚁金服上申请开通开发者账户,有了开发者账户就可以使用沙箱进行开发了。 在开发之前我们需要在沙箱应用中填写密钥,密钥的获取可以使用阿里提供的工具包自动生成。 前期准备工作完成了,接下来是编写代码部分。主要用到了python-alipay-sdk库,使用pip安装即可,如果安装的过程中遇到问题推荐使用Anaconda...

FanMLei的博客 8516

重构模式4

refactoring Patterns:第四部分 内容:不应 Refactoring 的场合实施Refactoring可能碰到的阻碍以及解决方案学习Refactoring获得短期效益削减Refactoring的额外开销安全

xxcc的专栏 1448

STM32CubeMX——循迹传感器TCRT5000的使用

前言 人生如逆旅,我亦是行人。 ————苏轼《临江仙·送钱穆父》 一、介绍 TCRT5000描述:(带有晶体管输出的反射光学传感器) TCRT5000是一种反射式光学传感器,它包括一个红外发射器和一个3.5mm引线封装的光电晶体管,可阻挡可见光。还具有1mA 典型被测输出电流,2.5mm峰值工作距离,可应用传感与仪表,工业等行业。 TCRT5000具有紧凑的结构,其中发射光源和探测器布置在同一方向,通过使用来自物体的反射红外光束来感知物体的存在。工作波长为950毫米,探测器由光电晶体管组成

W_Oilpicture的博客 1万+

Blender地形建模实战:5步搞定Gazebo仿真地形(附避坑指南)

本文提供了一份详细的Blender地形建模实战指南,旨在帮助开发者快速创建Gazebo仿真地形。文章通过五个核心步骤,从环境准备、地形雕刻、模型优化导出,到创建Gazebo模型包及问题排错,系统讲解了如何将Blender中的地形模型成功导入Gazebo进行机器人仿真,并重点分享了关键的避坑经验。

weixin_28475533的博客 344

杭州ERPTAO组织成功举办第一次技术讲座

2004年2月28日,在浙江大学软件学院和优快云网站的大力支持下,ERPTAO组织在浙大成功地举办了第一次软件技术讲座。有上百名专业软件开发者及爱好者到场参加,两位主讲人熊节(也就是我本人)和石一楹为大家送上了关于重构思想及O/R mapping技术的演讲,现场气氛相当热烈。稍后我们会按照签到表上的email地址给到场听众发送今天的讲义及相关资料,并在网上提供下载。今后我们会不定期地经常举行

透明思考@优快云 2510

COMSOL解的继承

COMSOL解的继承1. 应用场景2. 实现方法2.1. 添加研究步骤2.2. 引用研究 根据参考文献整理而来,原文见: http://blog.sina.com.cn/s/blog_4a0a8b5d010126w5.html https://www.zhihu.com/question/371867999/answer/1309194094 个人学习笔记,如有疏漏,欢迎补充 1. 应用场景 在COMSOL模拟中,一次求解之后往往遇到以下几种情况需要用到解的继承: 1)瞬态求解的继续运算。例如初始设定求解1

jessica0307的博客 1万+

重构模式5

refactoring Patterns:第五部分 内容:增量迭代Moving Target拥抱变化-Refactoring和XP参考资料关于作者相关内容:该系列的其他部分

xxcc的专栏 1284

运动目标成像必学:5分钟搞懂keystone变换原理与避坑指南

本文深入浅出地解析了雷达信号处理中的Keystone变换原理,它是解决运动目标距离徙动问题的关键技术。文章通过直观比喻和简化公式,阐述了该变换如何通过变量代换实现距离与慢时间的解耦合,将倾斜轨迹“掰直”,从而提升成像与检测性能。同时,文中重点指出了实际应用中关于大速度场景、插值误差、多目标处理及高阶运动补偿等关键避坑点,为工程实践提供了宝贵指南。

ios99的博客 403

nRF52832——大量数据传输时导致蓝牙断开连接,且无法被搜索到的解决方案(广播参数的设置、程序设计方法)

有时蓝牙通信单次发送数据量较大,且连续发送多次的时候,你会发现蓝牙就会出现异常,可能运行30min,40min,或者1h后,蓝牙就不能传输数据了,且无法被搜索到,用主机扫描蓝牙时可能会提示类似这样的DEBUG信息:bta_gattc_mark_bg_conn unable to find the bg connection mask for bd_addr=xx:xx:xx:xx:xx:xx。

数字世界 万物互联 2004

Role分析模式(二)角色对象创建和管理

概要Role Object(一)提出对解决Role问题的基本方法,但是我们还有许多问题需要深入,这些问题包括如何创建具体的Role实例,如何管理这些角色并实现角色之间的约束。 Role Object提出了对解决Role问题的基本方法,但是我们还有许多问题需要深入,其中的一个问题是如何创建具体的Role实例。动机对Role的创建有几个基本的需求。前面我们使用角色的名字作为Specification来

ivanlovetracy::伊凡::的专栏 1894

【史上最全面esp32教程】oled显示篇

esp32-arduino Oled屏幕的使用

m0_62599305的博客 1万+

Refactoring Patterns (Sixth Part)

refactoring Patterns:第六部分内容:简化设计设计模式关于作者相关内容:该系列的其他部分Refactoring和软件设计 石一楹 (shiyiyi

天行健 ● 君子以自强不息 1310

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

重构与模式(Refactoring to Patterns)-读书笔记

第一章 ☛过度设计:是指代码的灵活性和复杂性超出所需。 第二章 ☛重构是一种保持行为的转换。 第三章 ☛每一个模式都是由三部分组成的规则,他表达的是某一环境,一个问题以及解决问题的方案之间的关系。 ☛模式痴迷:对模式过于依赖。 第四章 ☛代码坏味道 ●代码重复(Duplicated Code) 通常可以通过形成Templ...

weixin_30693683的博客 242

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

05 claude code 提示词如何构建

所有模板均支持通过无硬性长度限制:提示词不规定压缩比例(如50%),仅要求信息完整性。双重防呆机制:Preamble 和 Trailer 双重禁止工具调用,确保压缩任务原子性。结构化输出:强制使用<analysis>和<summary>标签,便于后处理和审计。场景化区分:BASE(全量)、PARTIAL(局部)、UP_TO(前瞻)三种模板应对不同压缩需求。

m0_73980980的博客 696

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

用五年时间跨越鸿沟——“大学未毕业程序员”的流水帐

(本文发表于《程序员》2007年第2期,发表时有删节。) <!-- @page { size: 8.5in 11in; margin: 0.79in } P { margin-bottom: 0.08in } H1 { margin-bottom: 0.08in } H1.western { font-family: "A

透明思考@优快云 1万+

重构模式2

refactoring Patterns:第二部分 内容:简化测试更简单的设计Refactoring增进软件可理解性Refactoring 改进了软件的设计Refactoring 帮助你寻找BugRefactoring 让您

xxcc的专栏 1470

重构模式6

refactoring Patterns:第六部分 内容:简化设计设计模式关于作者相关内容:该系列的其他部分Refactoring和软件设计 石一楹 (shiyiy

xxcc的专栏 1427

软件工程 实践者的研究方法 第16章答案

Problem: Discuss the three “parts” of a design pattern and provide a concrete example of each from some field other than software. Answer: A design pattern can be characterized as “a three-part rule which expresses a relation between a certain context,

1万+

争议很大的问题

大家好,早上看到读者在群里讨论这些面试题:其中,第一个问题「在 4GB 物理内存的机器上,申请 8G 内存会怎么样?」存在比较大的争议,有人说会申请失败,有的人说可以申请成功。这个问题在没有前置条件下,就说出答案就是耍流氓。因为在 32 位操作系统和 64 位操作系统场景下,答案是不同的。另外,我们还要看申请完 8G 内存后会不会被使用,会被使用是一种情况,不会被使用又是...

程序员小灰的博客 402

重构模式(一)---- 介绍

                 石一楹 (shiyiying@hotmail.com)        浙江大学灵峰科技开发公司技术总监        2001 年 12 月                这是关于refactoring思考的第一部分内容。本文将介绍refactoring的基本概念、定义,同时解释正确、安全进行refactoring需要坚持的几个原则。               

幽客独往 2348

敏捷中国史

内容亮点 敏捷发展亲历者与推动者、资深程序员熊节作品 2000 — 2019 中国软件工程 20 年发展关键脉络 敏捷方法如何从草根流派到备受青睐,再到存有争议 无数资深老程序员上下求索突破技术瓶颈的编程岁月 首次集中呈现 IT 巨头阿里、腾讯、华为的敏捷之路 专家推荐 在我看来,软件研发人员大概可以分为四类:知道自己正在变得敏捷的践行者、知道自己并不敏捷的鼓吹者、不知道自己其实已经很敏捷的草...

GitChat 4465

参考函数

在前三课,你学到了传递的参数的值,参考函数,和地址。在本节中,我们将考虑返回值返回给调用者通过所有三个方法问题。 事实上,从一个函数的地址值,它的调用者返回值,或参考作品几乎完全相同的方式作为参数传递给一个函数是。所有的好处和缺点的每个方法都存在。两者之间的主要差别是,数据流的方向是相反的。然而,有一个更增添了几分复杂,因为在一个函数的局部变量超出范围的函数返回时,我们需要考虑这种效果在每个

u014260366的专栏 670

重构模式3

refactoring Patterns:第三部分 内容:应用Refactoring需要考虑的问题数据库接口改变和Published Interface用Refactoring思想武装自己的设计编程语言支持Refactori

xxcc的专栏 1418

从用户接触到完成需求说明书

从用户接触到完成需求说明书 内容:前言到用户前的准备需求调研 相关内容: TCP/IP 介绍

flyelf的专栏 1428
上一篇: refactoring Patterns第七部分
下一篇: refactoring Patterns第五部分
flyelf
博客等级 码龄25年 261粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值