企业应用开发考试笔记——重点

软件体系结构

   

1992 Dewayne Perry和Alex Wolf:软件体系结构由具有特定形式的体系结构元素(elements)或设计元素构成,包括处理元素、数据元素和连接元素三类。(或模块间的控制关系)

Soni, Nord和Hofmeister在研究了工业应用中有影响的一些流行结构后提出:软件体系结构至少有四个从不同方面描述系统的实例化描述:

设计元素和他们之间的关系;两个相互耦合功能分解和分层结构;反映系统动态性能的执行结构;描述源代码、二进制和库的组织的代码结构。

1997年,Bass, Clements和Kazman:软件体系结构包括部件、部件的外部可见性以及相互的关系。可见性是指部件提供的服务、性能、特性、错误处理、共享资源使用等。该定义强调体系结构须从系统中抽象出用于分析、决策的信息

理想中的软件开发

客户角度来看:软件系统的实施能够提升客户公司的生产力(生产/管理/效率/规范);软件公司能够准确领会我的意图(需求);软件公司有实力按时按质完成任务;以我为主,软件公司要适应我的进步和变化

软件公司来看:开发目标明确(前瞻+适应变化);各团队分工明确、发挥各自所长(内部的有效管理);人员良性流动与公司稳定;开源节流

利益相关者(Stakeholder)在软件项目中存在利害关系的人。即任何受到系统影响或对系统开发产生影响的人,都是利益相关者。

1.客户(决策者、管理者、使用者);2.开发者(系统分析员/顾问、架构设计师、开发工程师、测试工程师)

软件开发出的成果物必须为利益相关者提供切实的好处,否则它将会失败

1.本质问题(essence):软件本身在分析与设计上存在先天困难,即如何从抽象性问题发展出具体概念上的解决方案。(工艺或艺术)

2.实现时碰到的意外事件(accident):将概念上的构思施行于计算机上遇到的困难

造成本质性困难的原因 -1

本质性困难最难以解决,因为大部分活动是发生在人们的意识里,缺乏有效的辅助工具。

复杂性:软件系统所要解决的问题涉及很多的实体、关系、流程,这些问题的解决需要人的抽象化的智能活动;不可见性:在完成开发前,软件是不可见的,即使使用图示说明,仍无法充分呈现完整结构和重点细节,使得人们在沟通上面临极大困难

造成本质性困难的原因 -2

本质性困难最难以解决,因为大部分活动是发生在人们的意识里,缺乏有效的辅助工具。

协同性:在软件系统中,各子系统、模块的接口必须协同一致。由于时间和环境的演变,维持一致性十分困难;变异性:软件所应用的环境常是由人群、法规、硬件设备、应用领域等,各因素所汇集而成,而这些因素皆会快速变化。

造成本质性困难的原因 -3

软件系统是社会系统,是社会活动(生产/管理/社交)的抽象、深入和延续;

软件系统的开发就是刻画社会系统的过程,开发的软件系统如果不能融入现有社会系统中,它的生命就终结了。

造成本质性困难的原因 -4

1.客户端:客户需求被误解、或未被完全捕获;客户的需求改变的过于频繁;客户没有准备为项目提供足够的资源;客户不想与开发者合作;客户怀有不切实际的期望;系统不再对客户有利;

2.解决:随着软件复杂性的增加,人们意识到,开发者是最重要的;“伟大的设计来源于伟大的设计者”

第一范式(1NF):1.实体中的某个属性不能有多个值或者不能有重复的属性。2.通俗:一个实体不能有重复列,一列只能表示实体的一个属性

第二范式(2NF:满足1NF;每个实例(或行)必须可以被唯一地区分;通俗:不能有重复的行,要有索引列(又称主键:可以是多列,保证唯一)

第三范式(3NF:满足2NF;如果关系模式中的所有非主属性对任何候选关键字都不存在传递信赖,则称关系R是属于第三范式的 ;通俗:若将一个实体分为主键和相关信息,那么关于实体的相关信息只能出现一次。(去除冗余信息)

BCNF(鲍依斯-科得范式):如果关系模式R(U,F)的所有属性(包括主属性和非主属性)都不传递依赖于R的任何候选关键字,那么称关系R是属于BCNF的;典型情况:主属性与候选主键间存在传递依赖关系

触发器:1.简单的数据检查:规则和引用完整性约束

2.功能更强大的解决方法:触发器

是一段小程序;挂接到一定的查询动作上;多用于数据检查、汇总更新上

3.缺点:触发器的性能开销较大;触发器的多级触发不好控制,容易发生错误;

存储过程:为查询语言添加应用开发能力(数据库中的编程语言)---结构化查询语句SQL;通用程序设计语句:变量、循环、分支以及赋值语句等;输入、输出;除数值类型外,支持复杂数据类型、数组的定义

关系模式可以形式化地表示为:R(U,D,dom,F)

R:关系名(表名);U:组成该关系的属性名集合 (列名);D: 属性组U中属性所来自的域 ;dom:属性向域的映象集合;F: 属性间的数据依赖关系集合

数据建模的过程

1.确定数据及其相关过程(如实地销售人员需要查看在线产品目录并提交新客户订单)。 2.定义数据(如数据类型、大小和默认值)。 3.确保数据的完整性(使用业务规则和验证检查)。 4.定义操作过程(如安全检查和备份)。 5.选择数据存储技术(如关系、分层或索引存储技术)。

数据访问层(DAL)

1.目的:将系统所需信息从各种存储介质/格式中读取,并转换成内存中的对象,供上层自由使用。

2.常见的来源:数据库(Oracle,SQL Server,DB2);XML 文档 / 普通文件;Message Queue

3.常见的开发方式:直接调用数据库API函数(如ADO.NET /JDO);将这些API封装起来,以ORMapping方式提供(对象/关系映射)

MVC模式(Model-View-Controller是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller),模型—视图—控制器模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

Model:用于表示的业务数据

View:用于业务数据的显示

Controller:根据用户的操作控制业务逻辑(通过数据及其显示来表示)

MVC总结:

1.分而治之是核心理念;2.分层的核心理念是将封装和交互---封装:屏蔽一切与外界无关的细节;和底层的交互,只能调用底层提供的服务(公开出来的服务,无法访问封装起来的一切);和上层的交互,只向上层提供最必须的基础服务

3.分层好坏的评估:层内耦合最大(封装为内部实现)、层间耦合最小(暴露为服务、接口); 学习、记忆、使用成本都会降至最低(函数最少、参数最少)

DAL:获取ER Model(Get relation model);Create class to describe objects;Accessingand Transforming;

1.存储过程使用的好处:数据库服务器提供的支持(事务、游标…);减少网络通信,提升速度;分离数据库开发和系统开发2.不使用的好处:便于移植;除SQL语句,不需要学习过多的与数据库相关的知识

数据生命周期:

1.同软件系统一样,数据也是有生命周期的:

临时、会话、系统、持久层:保持当前状态持久;保持所有历史状态持久

2.数据的生命周期是一件影响长久的因素,在系统分析与设计的时候要仔细考虑。

数据的生命周期:1.当前系统中持久数据的快照2.当前系统中持久数据现状及历史的记录

控制反转(Inversion of Control, IoC

面向对象编程的法则来削减计算机程序的耦合问题。Martin Fowler称之为依赖注入(Dependency Injection, DI)

不改核心类的实现;能够依赖条件进行动态装载(使用代码完成);不需要重新编译所有代码,依靠配置文件的修改完成依赖条件的修改(将代码里的条件判断用XML描述);趋势:相关逻辑控制的脚本化(多采用XML配置文件)

IoC的手段:关联脚本化(XML);实例创建工厂化;实现技术:反射Reflection

AOP(Aspect Oriented Programming):面向切片/服务编程

Authorization授权/Authentication认证

1.OOP从对象角度, 通过引入封装、继承和多态性等概念来建立一种对象层次结构。对公共属性、行为进行封装和有效复用。2.AOP从流程角度,针对业务处理过程中的某个关键点(步骤或过程)进行再编程,以获得逻辑过程中各部分之间低耦合性的隔离效果。

AOP面向切片/服务的编程,在Spring中使用最多的是对事物的处理。而AOP这种思想在程序中很多地方可以使用的,比如说,对某些规则的验证,可以抽象封装到一个模块中,并在该模块中定义一定的使用规则,然后植入到原有的程序中,其实这就是面向切片。这个模块叫做Aspect,定义的规则是pointcut,具体的验证的实现是advice,植入的目标叫TargetObject,切入到目标中的方法叫做joinpint,植入的过程叫weave。

BLL层中的设计模式(策略模式);

BLL的开发趋势:

1.基于接口的编程:根据用户交互UI和业务逻辑的衔接来制定功能及对应的参数 2.基于SOA的呈现:将业务逻辑层封装的服务(功能)以不同的访问方式共享出去--Web Service(Web服务)/RPC(远程过程调用) 3.支持事务 (Spring, MS MTS):数据库的回滚;异常的统一处理 4.支持业务注入(AOP,面向方面的编程):事务回滚;认证;授权;状态管理(Session /Application / Lock)

事务(transaction):在企业应用中处理并发的最主要的工具;具有明确边界的工作序列,并要求事务要么执行完毕,要么恢复至开始状态,不允许停留在中间状态

事务是指一个单元的工作,这些工作要么全做,要么全部不做。数据库中的事务必须具备四个属性(ACID) :原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

数据库/软件事务的属性:

原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性:在事务开始之前和事务结束以后,数据库的完整性限制没有被破坏。

隔离性:两个事务的执行是互不干扰的,一个事务不可能看到其他事务运行时,中间某一时刻的数据。

持久性:在事务完成以后,该事务对数据库所作的更改便持久地保存在数据库之中,并不会被回滚(Rollback

良好的用户界面设计原则:

1.用户控制:以用户为中心,将界面置于用户的控制之下,用户应该总是感觉在控制软件而不是感觉被软件所控制。采取交互式和易感应的窗口;从用户的视角出发,使用用户的语言;后台运行长进程时,保持前台的交互性;容忍用户探索中出错;提供用户自定义设置

2.一致性:遵循标准和做事情的常规方式,例子:Windows系统及其成功;减少学习成本:让用户处于一个熟悉的和可以预见的环境中(油门和刹车的位置)

/登录组件:焦点在“用户名”;焦点在“密码”;回车后自动按动“登录”

3.个性化和定制化

4.允许尝试:一个好的界面应该允许用户进行实验和出错,而能容忍且不崩溃

5.反馈:要实现用户控制,就应该让用户知道当控制交给系统后将会发生什么,并在适当的时候提示给用户

6.审美和可用性:黄金分割、人眼的凝视和移动、颜色的使用、平衡和对称、元素排列和间隔、相关元素的分组

几类典型Web系统的设计:MIS/ERP 管理信息系统;CMS (内容管理系统);B2B/B2C(Business 2 Business);Portal

Web设计:主页设计;页面框架设计;主从信息展示

首页版式:1)两列式;2)三列式:门户类、多种索引列;3)入口式

首页设计准则:

1.展示的应该是网站最有价值的部分:最重要的;最新的;重要程度要和显示面积成正比;重要程度要应和浏览花销成反比

2.基于用户体验的首页模块调整:基于日志的用户体验的挖掘;基于界面动态调整的Portal框架

Darwin Stream Server:

简称DSS。DSS是Apple公司提供的开源实时流媒体播放服务器程序。

DSS的核心服务器部分是由一个父进程所fork出的一个子进程构成,该父进程就构成了整合流媒体服务器。父进程会等待子进程的退出,如果在运行的时候子进程产生了错误从而退出,那么父进程就会fork出一个新的子进程。

可以看出,网络客户和服务器直接的对接是由核心服务器来完成的。网络客户RTSPoverRTP来发送或者接受请求。服务器就通过模块来处理相应的请求并向客户端发送数据包。

核心流媒体服务通过创建四种类型的线程来完成自己的工作,具体如下:

MainThread服务器自己拥有的主线程。当服务器需要关闭检查,以及在关闭之前记录相关状态打印相关统计信息等任务处理时,一般都是通过这个线程来完成的。

IdleTaskThread空闲任务线程。这个任务线程是用来对一个周期任务队列的管理,主要管理两种任务,超时任务和Socket任务。

EventThread事件线程。套接口相关事件由事件线程负责监听,当有RTSP请求或者收到RTP数据包时,事件线程就会把这些实践交给任务线程来处理。

TaskThread任务线程。任务线程会把事件从事件线程中取出,并把处理请求传递到对应的服务器模块进行处理,比如把数据包发送给客户端的模块,在默认情况下,核心服务器会为每个处理器核创建一个任务线程。

Darwin服务器的Reactor模式:

Clients -- Core Server -- Modules

在DSS的框架设计中采用了Reactor设计模式:

MainThread: Reactor模式中的Reactor(InitialDispatcher)

EventThread: Reactor模式中的Event Demultiplexer

TaskThread: Reactor模式中的Event Handler

Reactor设计模式:

Reactor是一种事件驱动机制:将相应功能注册到Reactor(可看成是MessageMapper)上;当事件发生时,Reactor将主动调用相关功能注册的响应函数,这些接口又称为“回调函数”。

1)Handle (句柄)

Handle代表操作系统管理的资源,包括:Socket,FileHandler,Timer,同步对象等等;程序在指定的句柄上注册关心的事件,比如I/O事件。

2)Event Demultiplexer (事件分离器)

在linux上主要由select, poll, epoll等系统调用进行实现,在一个Handle集合上等待事件的发生;例如,接受client连接,建立对应client的事件处理器(Event Handler),并向事件分发器(Reactor)注册此事件句柄

3)Reactor(Initiation Dispatcher,事件管理)

注册、删除和派发Event Handler。EventDemultiplexer等待事件的发生,当检测到新的事件,就把事件交给Reactor,它去回调Event Handler。

4)Event Handler

负责处理特定事件的处理函数。一般在基本的Handler基础上还会有更进一步的层次划分,用来抽象诸如decode,process和encoder这些过程。

5) Concrete Event Handler

继承EventHandler类,实现钩子方法。应用把ConcreteEvent Handler注册到Reactor,等待被处理的事件。当事件发生,这些方法被回调;利用多路分离(Demultiplex)函数的这一特点,根据被激活的句柄对应的特定事件,调用相关的事件处理函数。可以实现事件循环。

基本模式:

Gateway模式:An object that encapsulates access to an external system orresource---1.封装对外部功能和资源的访问--入口参数;返回值;异常

2.将外部接口对系统的影响降至最低(修改最少)

Plugin模式:使你开发的应用尽可能地适应多个同质外部环境,降低对外部细节的依赖---配置信息(常用XML);实例创建工厂化;技术基础:在支持反射(Reflection)的高级语言下可被很好地支持;在没有支持的语言环境下,使用条件逻辑来将接口映射到实现,可将工厂放到一个独立包中减少编译负担

Money:Motivation---一种看起来简单,但实际问题很多的数据类型

1.货币兑换(买入、卖出、中间价、手续费)2.精度问题(收支平衡与取整模式,账面值与支付值)

Service Stub:Removes dependence uponproblematic services during testing---首先使用Gateway来定义一个服务(替代尚未完工的功能或第三方服务);Gateway应该以Plugin的方式载入(降低日后更替带来的风险);将典型Use-Case实现于Service Stub中;将罕见的情况实现于Service Stub中

Mapper模式:An object that sets sup a communication between two independentobjects;Mapper是子系统间的绝缘层,控制着子系统间的通信细节,且尽量不被子系统感知;在企业级应用中,Mapper经常用于数据库的交互(数据映射器)

 

分布模式:

Remote Façade(远程外观):在细粒度对象上提供粗粒度的外观,以提高网络的效率。(小粒度对象间的频繁交互不利于降低网络传输消耗)功能:将粗粒度的方法转换到低层的细粒度对象上

数据传输对象(Data TransferObject):序列化数据传输对象;从领域对象组装一个数据传输对象

Web Service核心

SOAPSOAP(Simple Object Access Protocol,简单对象访问协议)是一个基于xml的协议,用于在分步的应用程序都可以识别。另外,SOAP本身没有定义任何程序语言,这使得SOAP能够以消息的形式传递到各种远程系统中。

SOAP所使用的传输协议,可以是HTTP,SMTP,POP3,JMS。

SOAP包括了4部分:

   01.“SOAP封装(Envelope)”:定义一个描述信息描述的内容是什么,是谁发送的,谁应当处理他,以及如何处理他们的框架。

  02.“SOAP编码规则”:用于表示应用程序需要使用的数据类型的实例。

  03.“SOAPRPC": 表示远程过程中调用和应答的协定。

  04.“SOPA绑定”:使用底层协议交换信息。

WSDL

WSDL(Web Service Description Language,web服务描述语言)是一个XML文档,他以一种和具体语言无关的抽象方式定义了给定web服务收发者的有关操作和消息。

乐观并发控制:

在乐观并发控制中,用户读取数据时不锁定数据。当一个用户更新数据时,系统将进行检查,查看该用户读取数据后其他用户是否又更改了该数据。如果其他用户更新了数据,将产生一个错误。一般情况下,收到错误信息的用户将回滚事务并重新开始。这种方法之所以称为乐观并发控制,是由于它主要在以下环境中使用:数据争用不大且偶尔回滚事务的成本低于读取数据时锁定数据的成本。

做副本,通过副本上的修改痕迹进行同步,已达成工作的并发进行;如SVN和CVS的区别;如ADO.NET中DataSet.AcceptChage,DataSet.MergeChange;实质是冲突检测与解决

悲观并发控制:

一个锁定系统,可以阻止用户以影响其他用户的方式修改数据。如果用户执行的操作导致应用了某个锁,只有这个锁的所有者释放该锁,其他用户才能执行与该锁冲突的操作。这种方法之所以称为悲观并发控制,是因为它主要用于数据争用激烈的环境中,以及发生并发冲突时用锁保护数据的成本低于回滚事务的成本的环境中。

ADL定义:

ADL是这样一种形式化语言,它在底层语义模型的支持下,为软件系统的概念体系结构建模提供了具体语法与概念框架。基于底层语义的工具为体系结构的表示、分析、进化、细化、设计过程等提供支持。

其三个基本构成元素是:1)组件—— 计算或数据存储单元;2)连接子—— 用于组件问交互建模的体系结构构造块及其支配这些交互的规则;3)体系结构配置—— 描述体系结构的组件与连接子的连接图。

ADL组成:

1.   Componen:抽象级别上组成系统的计算模块。物理上的具体软件元素、编译单元、抽象概念

2.   Operators:Component间的交互机制,被认为是将结构元素连接成更高级Component的功能

3.   Patterns:结构元素按照特定方式进行的组合。可以将Pattern看做针对一个特定问题的设计模板,并且,模板将体现元素选择与交互的限制。

4.   Closure:用于实现分层描述的概念

5.   Specification:用于说明功能、性能、约束(容错能力等)的规格

常用的软件质量属性:

可修改性,可靠性(可用性),性能,可测试性,易用性,安全性

软件架构分析方法SAAM: Scenarios-basedArchitecture Analysis Method

(基于场景的架构分析方法)

SAAM的一般步骤:

1.   场景开发 2. 描述体系结构 3. 对场景分类和设置优先级 4. 间接场景的独立评估 5. 识别场景关联 6. 形成整体评估

SAAM – 场景生成:

1.除客户方参与,开发人员也参与,各自提供不同的视角观点:每个观点的优先级、开发费用、难度、繁杂程度等;有可能出现相互矛盾的场景(功能间的矛盾、客户与开发人员的矛盾、软件与硬件间的矛盾);记录为主、裁决为辅

2.场景好坏的衡量---好场景:这些场景反映了系统的主要用例、潜在修改、更新、或系统行为能被相对容易的度量所刻画

SAAM – 体系结构描述

描述准则:被多种角色所接受;对构件、连接器、模块、配置、依赖、部署等概念能界定清楚

SAAM-场景分类与优先级确定

场景可分为

1.直接场景:当前体系结构不经修改即可支持的场景(从需求中很容易找到对应的Use Case

2.间接场景:不能被直接支持,于是需对体系结构进行修改,如添加组件、去除间接层、替代.改变.增强接口、改变实现模式等;其改变对环境和后续设计、开发的影响深远;委员会式的评估、投票

SAAM-对场景关联的评估:

一般来说场景关联是灾难的种子;系统结构变更后,这些关联会导致修改混乱;解决---减少关联的场景(去除伪关联),确认关联方带来的影响

ATAM:Architecture Tradeoff Analysis Method (架构权衡分析方法)

ATAM目的:

我们需要一个新方法,让我们能尽早提出正确问题,来:

发现风险:可能在将来产生质量问题的方案;

发现非风险:可以提高质量的决策

发现敏感点:方案中一个小小的变化,就可能让质量完全大变样;

发现折中:影响一个以上质量的决策

ATAM Steps

1.介绍ATAM;2.讲解商业动力;3.讲解体系结构;4.明确体系结构方法;5.生成有效树;6.分析体系结构方法;7.自由讨论和为场景排序;8.分析体系结构方法;9.讲解结论

1. Present the ATAM

Evaluation Team presents an overview of the ATAMincluding:

ATAM steps in brief

Techniques:utility tree generation(效用树生成);architecture elicitation and analysis(体系结构引出和分析);scenario brainstorming/mapping(场景讨论/映射)

Outputs:architectural approaches;utilitytree;scenarios;risks and “non-risks”;sensitivity points and tradeoffs

2. Present Business Drivers

客户代表描述系统的商业动力

Business context for the system

High-level functional requirements

High-level quality attribute requirements:1.体系结构动力:质量因素塑造体系结构2.苛刻需求:对系统的成功有决定作用的质量

3. Present Architecture

(1)架构师对体系结构的简介:技术限制,比如必须要采用的OS、硬件和中间件;其他必须与之交互的系统;用来满足质量需求的体系结构风格

(2)架构师、项目经理和市场代表一起来描述此系统如何为公司带来价值:市场代表必须详细阐述系统的功能和质量需求对市场价值的影响;项目经理必须详细阐述体系结构需要的成本

4. Identify Architectural Approaches

开始确认体系结构中对实现质量需求产生决定作用的部分;明确主要的体系结构方法;Examples:client-server、3-tier、watchdog、publish-subscribe、redundant hardware

5. Generate Quality Attribute Utility Tree

(1)通过建立一个效用树,来明确、排序和精炼大部分的质量目标:效用树是一个自顶向下的工具,用来刻画重要的需求;把最重要的质量目标放在高层节点(典型的有:性能、适应性、安全和可用性);Scenarios are the leaves of the utility tree (2)输出:质量需求的描述和优先级

ATAM的特点:

ATAM的目标不是做精确的分析,而是发现体系结构可能带来的风险;我们要发现一些趋势:从体系结构方案预言系统的特性;发现风险,然后做进一步的分析、设计;明显的折中可以被清晰地指出并写入文档

Evaluation Team

每个ATAM组有一个组长和至少三个组员

领域专家不是必须

ATAM组员必须是经验丰富的架构师

ATAM组长必须有优秀的交流和激励技巧

ATAM组员在评审过程中扮演多种角色

Evaluation Team Roles

主持人:推动讲解、自由讨论和分析

场景记录员:在白板上记下原始场景、有效树、风险、关键点和折中

会议记录员:把场景记录员写下的内容录入电脑,准备结论讲解模板

过程实施者/观察者:监视各个步骤,做笔记,寻找改进方法

计时员:当某一个步骤的时间已经超出时,提醒组长

提问者:发现各个角色还没有想到的问题;询问质量因素怎样和体系结构风格关联的问题

Basic Rules for ATAM Team Members

让过程持续进行;提问;提出建议性的场景;记下各个角色所说的话,但是不要改写

Results of the ATAM
极大地改善了体系结构文档;Stakeholder buy-in;发现了被漏掉的性能和可用性需求;突出了以前没被意识到的体系结构折中点;提出减轻折中的风险的建议

Summary

1.ATAM是一个根据质量需求评审体系结构的方法

2.对发现建筑决策的结果来说ATAM是一种有效的策略。一、ATAM可以针对开发早期分析,还可以对旧系统分析。二、ATAM分析开销小。三、建立利益相关者的信心和买进。

3.方法的关键是寻找趋势,而不是做预言式的分析

The ATAM relies critically on:(ATAM依赖于)

客户的有效准备;清晰的质量需求;角色的积极参与;架构师的积极参与;对体系结构风格和分析模型的熟悉


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值