已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
目录目录 目录目录 .1 SAP 开发规范开发规范.3 1说明说明.3 1.1内容说明 .3 1.2规范目的 .3 1.3使用说明 .3 1.4使用对象 .3 2一般规则一般规则.3 3代码管理代码管理.4 3.1程序标题 .4 3.2子程序、模块标题 .4 3.3编辑器设置 .5 3.4代码格式 .6 3.4.1使用规范化打印机.6 3.4.2查询SQL语句的写法.6 3.5变更记录管理 .7 3.6代码注释 .7 3.7子程序与函数模块 .8 3.8其它注意事项 .8 4数据库查询数据库查询 .8 4.1不要在 LOOP循环中使用 SELECT语句.8 4.2取数的时候不能使用 SELECTENDSELECT语句循环操作.8 4.3尽量多使用内表 .8 4.4SELECT 与 SELECT*比较 .9 4.5外部检查 .9 4.6SELECT SINGLE语句使用注意.9 4.7SELECT 语句中排序 与 ABAP 语句中排序比较.9 4.8SELECT DISTINCT语句使用.10 4.9批量更新数据库表 .10 4.10FOR ALL ENTRIES 语句.10 4.11OPEN SQL 与 NATIVE SQL 比较.11 4.12表连接 .11 5内表使用注意内表使用注意.11 5.1内表定义 .11 5.2内表使用 .11 5.2.1修改内表中的字段值.11 5.2.2把一个内表附加到另一个内表后面.11 5.2.3删除内表中重复行.12 5.2.4根据条件删除内表中的行.12 5.2.5内表是否为空的判断.12 5.2.6读取内表行.12 5.2.7通过LOOP AT it_tab ASSIGNING 循环内表.13 5.2.8通过平行光标来连接两个内表.13 5.2.9释放内表.14 6数据字典对象数据字典对象.14 6.1建表规则 .14 6.2创建数据元素/域的基本规则.14 6.3添加客户化字段到 SAP 表中 .14 6.4索引维护 .15 7文件处理文件处理.15 8SMART FORM .16 9权限权限.16 10其它注意事项其它注意事项.16 10.1消息类使用 .16 10.2子程序参数传递 .16 10.3局部变量与全局变量的使用比较.16 11代码检查代码检查.18 12ABAP 性能例子性能例子.18 SAP 开发规范开发规范 1说明说明 1.1 内容说明内容说明 开发规范总的包含了以下三方面的内容 SAP 开发规范 ,即本文档,主要内容为: 说明开发中要注意的事项 代码编写的规范 开发中要遵循的规则 性能有关的例子 SAP 开发命名规范 ,主要为开发过程中涉及的命名规则 涉及开发中的命名,统一命名规则,方便程序的沟通、阅读 1.2 规范目的规范目的 提高 SAP 项目中开发的效率、质量,实现标准化管理 1.3 使用说明使用说明 在客户有开发规范的情况下,请按照客户的开发规范进行开发 如客户未有现成的开发规范,请按照公司的开发规范进行开发 请确保在开发的时候遵循了以下规则,如模糊不清时,请及时重新查阅。如有冲突的 地方,请与项目上的开发负责人进行确认 1.4 使用对象使用对象 任何在 SAP 系统进行开发的人员 初学 ABAP 开发者 2一般规则一般规则 以下为开发过程必须遵守的一般规则,请遵守。 请严格按照SAP 开发命名规范进行相关命名 请遵循SAP 项目管理流程来进行开发及开发管理 在开发完成后,在开发系统中清除不需要的对象 不可随便修改 SAP 源代码,除非是客户化字段、用户出口等 若确实需修改 SAP 源代码,请与技术负责人确认,并进行严格记录、测试 在程序中不要遗留下没有使用的变量,可以使用”Extended Program Check”进行检查 当有文本输出时,通过维护文本符号来实现。使用文本符号方便翻译 使用常数来代替固定数值或固定字符 常数必须定义为全局的。优点:可重复使用,只需修改一个地方 请不要在代码中对组织架构(公司代码、工厂、采购组织等)的数据赋予固定值,组 织架构的值可通过参数(Parameter) 、选择范围(Select-options) 、变式等方式来传值 不要使用 TABLES 语句定义的数据结构,使用专门定义的工作区 TABLES 语句只用于 Select-options 需要的时候 尽可能不要使用 MOVE-CORRESPONDING 语句,因为比较耗时 在 SQL 语句执行完成后、内表操作语句执行完成、调用完函数等之后,检查返回码 sy-subrc 的值,并做判断处理 一般情况下为一个程序创建一个对应的请求,以防传输时混乱 慎用 ABAP 中的宏,因为宏不易维护且不可调试 所有的开发的程序必须分配对应的事务代码,从而可避免给用户分配 SA38 或 SE38 的 权限 原则上一个程序的代码不能超过 2000 行,超过 2000 行请使用 include program 确保可 读性 3代码管理代码管理 在创建一般报表时,请拷贝报表模板程序,并在模板程序的基础上进行代码编写。 模板程序主要包括了程序标题格式、事件关键字等内容。方便程序编写并统一规范。 3.1 程序标题程序标题 每个程序标题必须包括了程序名称、程序名、开发日期、开发者、概要、变更记录(日期、 变更者、传输请求号、修正内容) 。请参考以下例子: 例子例子 *-* * 程序名称:程序名称 * 程序名: ZZZ*_* * 开发日期:2012/MM/DD * 创建者:张三 * 申请者:李四 *-* *概要: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * XXXXXXXXXXXXXXXXXXXXXXXX *-* * 变更记录 * 2012/ins-day By 张三(Hand) DEVK90000 * Description: * 1.初始开发 * * 2012/ins-day By 张三(Yahua) DEVK90010 * Description: * 1.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * 2.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *-* REPORT ZZZ*_* NO STANDARD PAGE HEADING LINE-SIZE 255 LINE-COUNT 65. 注意:请将可能详细的维护好程序标题。 3.2 子程序子程序、模块标题、模块标题 在子程序定义前面必须进行注释说明。说明内容包含:包括子程序名称、子程序功能 描述、参数功能等。模块与子程序类似。 子程序例子子程序例子 *-* *Form GET_COMPANY_CODE_DESCRIPTION * *-* * 通过传入的公司代码,得到公司代码 描述 * *-* * 传入参数:fu_bukrs 为公司代码 *-* FORM get_company_code_description USING fu_bukrs CHANGING fu_butxt. 模块例子模块例子 *-* * MODULE SET_PFSTATUS OUTPUT * *-* * 设置屏幕 100 的 GUI 状态 * *-* MODULE SET_PFSTATUS OUTPUT. 注意:请将可能详细的维护子程序、模块的标题信息。 另:另: 原则上一个原则上一个 FORMFORM 的长度不能超过的长度不能超过 300300 行,如果超过行,如果超过 300300 行,请尽量拆分为多个行,请尽量拆分为多个 FORMFORM,确保程序的可读性。,确保程序的可读性。 如:要从如:要从 1010 个表中取数,我们可以将其拆分为多个个表中取数,我们可以将其拆分为多个 FORMFORM,其中取文本表的单独一个,其中取文本表的单独一个 FORMFORM,当从某个表取字段过多,当从某个表取字段过多( (或者取出即有部分处理逻辑或者取出即有部分处理逻辑) )时,我们甚至可以将其单独一时,我们甚至可以将其单独一 个个 FORMFORM。 3.3 编辑器设置编辑器设置 为了使原代码更易于解读,规定对编辑器进行设置。 设置路径为:SE38-实用程序(U)-设置(E) 编辑器:设置使用新的编辑器、每行最长 72 个字符长(注:抓图为 ECC6.0 例子,其它版 本请以实际为主) “规范化打印机(pretty print) ”设置:缩进、关键字大写 3.4 代码格式代码格式 3.4.1使用规范化打印机使用规范化打印机 要求在代码编写完成后,点来格式化代码,使代码自动缩进、关键字大写等, 在编写代码过程中使用该按钮,也有助于发现输入错误的语句。 注意:如果规范化打印机不可用,则手工的调整代码,使代码结构化。注意:如果规范化打印机不可用,则手工的调整代码,使代码结构化。 3.4.2查询查询 SQL 语句的写法语句的写法 Open SQL 请按如下格式对齐,有助于阅读和查看 SELECT field1 “xxx field2 “xxx INTO TABLE it_mara FROM MARA WERE matnr =wa_tab-matnr. SELECT,INTO,FROM 等缩进两空格 WHERE 缩进一空格 字段 field1,field2 每个都进行换行,并在字段名后 注释其意义 3.5 变更记录变更记录管理管理 当程序传到生产机之后,开始进行变更管理。变更管理开始后,进行程序的修改时要注意 以下: 在程序的开始处的变更记录上记录以下内容 日期日期修改者修改者传输请求号传输请求号修改内容及原因修改内容及原因 例子例子 *-* * 变更记录 * 2012/ins-day By 张三(Hand) DEVK90000 * Description: * 1.初始开发 * * 2012/ins-day By 张三(Yahua) DEVK90010 * Description: * 1.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * 2.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * *-* 在修改的程序代码处,要通过类似如下注释包括起修改的内容 *2012/01/08 张三 规格变更 Chg 修改开始 * 修改原因: * *2012/01/08 张三 规格变更 Chg 修改结束 例子例子 1 *2012/01/08 张三 规格变更 Ins 修改开始 * 修改原因: * *2012/01/08 张三 规格变更 Ins 修改结束 例子例子 2 *2012/01/08 张三 规格变更 Del 修改开始 * 修改原因: * *2012/01/08 张三 规格变更 Del 修改结束 修改标识修改标识意义意义 Ins新增 Chg修改 Del删除 注意:在删除代码时,只是注释,不要实际物理的删除代码。注意:在删除代码时,只是注释,不要实际物理的删除代码。 3.6 代码注释代码注释 数据声明和程序的逻辑部分必须要有注释,逻辑部分的注释尽量控制在整个程序代码 的 10%20%左右。 特殊的标记字段要在注释中说明其用法 根据项目语言使用情况选择用中文或英文进代码注释和文档说明 在初次提交的代码中不要保留初注释掉的代码行 在涉外的开发项目中,注释和说明要用英文 3.7 子程序与函数模块子程序与函数模块 程序中的代码要尽量实现模块化,即如果程序中多个地方将会用到相同的代码,那么 将这些代码封装在子程序中,以便可以随时调用,同时也增加了程序的可读性和可维 护性 当相同的功能的代码可以被多个程序使用时,最好定义成函数模块 避免调用外部程序中的子程序 3.8 其它注意事项其它注意事项 在每一个程序行中,最多只有一个语句,避免一条程序行中包含多条语句的写法。 4数据库查询数据库查询 在使用 SELECT 语句中,尽可能把握以下几个原则: 尽可能的在 Where 条件中包含多的索引字段作为条件 Where 条件中的索引字段按表中的索引顺序排列 尽可能的在 Where 条件中包含多的条件,即使字段不是索引的 4.1 不要在不要在 Loop 循环中使用循环中使用 Select 语句语句 在循环中使用 Select 语句将会频繁的访问表,尽量减少数据库访问的操作。 以下语句是不建议的。 LOOP AT it_tab INTO wa_tab. SELECT * FROM MARA INTO TABLE it_mara WERE matnr =wa_tab-matnr. ENDLOOP. 4.2 取数的时候不能使用取数的时候不能使用 SelectEndselect 语句循环操作语句循环操作 例: 错误错误正确正确 SELECT * FROM dbtab MOVE-CORRESPONDING dbtab TO it_ab APPEND it_tab ENDSELECT. SELECT * FROM dbtab INTO TABLE itab. 注意:必须避免嵌套的注意:必须避免嵌套的Select,因为每条记录将连接一次数据库。,因为每条记录将连接一次数据库。 4.3 尽量多使用内表尽量多使用内表 为了提高程序的性能,通常先把数据库表中的数据取到内表中,在内表进行数据处理。减 少重复访问相同数据表的情况。 例: (错误错误) (正确正确) 处理A 处理B 处理C 处理A 处理B 处理C 内表 数据库 数据库 I/O 程序流 4.4 Select 与与 Select*比较比较 尽可能使用 SELECT 来代替 SELECT *,减少数据传输量,当数据量大时,前者速 度会明显更快。 例: 不建议写法不建议写法建议写法建议写法 SELECT * FROM dbtab INTO TABLE it_tab WHERE . SELECT field1 field2 FROM dbtab INTO TABLE it_tab WHERE . 仅用于某种特殊情况时,如表的字段数很少 或数据记录不多 当表中包含的大量字段时 使用 Select 虽然会增加代码的工 作量,但会减少内存的使用及网络的负 担 4.5 外部检查外部检查 尽可能避免使用 Select COUNT (*)来确认表存在的记录数,因为该语句将会对表进行全扫 描。 4.6 Select single 语句使用注意语句使用注意 当使用 Select Single 时,要求在 where 条件语句中包含所有的关键字 当不能确定所有关键字而能确定只有一条记录匹配时,可使用Select . up to 1 rows来 查询 4.7 Select 语句中排序语句中排序 与与 ABAP 语句中排序比较语句中排序比较 对数据排序有两种方式:一种为在数据库服务器中排序;一种为在应用服务器中排序。 不建议写法不建议写法建议写法建议写法 SELECT field1 field2 FROM dbtab INTO TABLE it_tab WHERE. ORDER BY field1. SELECT field1 field2 FROM dbtab INTO TABLE it_tab WHERE . SORT it_tab BY field1. 应用服务器排序更快 4.8 Select distinct 语句使用语句使用 不建议写法不建议写法建议写法建议写法 SELECT DISTINCT field FROM dbtab INTO TABLE it_tab WHERE. SELECT field FROM dbtab INTO TABLE it_tab WHERE . SORT it_tab BY field1. DELETE ADJACENT DUPLICATES FROM it_tab COMPARING field. 需要在数据库服务器进行排序,且 会影响整个系统性能如果没有合适 的索引 使用 DISTINCT 语句时,数据库服 务器经常不访问缓存 .尽可能避免在 SELECT 语句中使用 GROUP BY, ORDER BY , DISTINCT 等功能。 4.9 批量更新数据库表批量更新数据库表 在更新数据库数据时,可以一条一条记录的更新,也可批量的更新多条记录。当更新记录 数较多时,请采用批量更新的方式,有助于性能的提高。 不建议写法不建议写法建议写法建议写法 LOOP AT it_tab INTO wa_tab. UPDATE dbtab SET field01 = wa_tab-field01 WHERE field2 = wa_tab-field2. ENDLOOP. LOOP AT it_tab INTO wa_tab. ENDLOOP. UPDATE dbtab FROM TABLE it_tab. 执行完成更新语句后,会有两个比较有用的系统变量值返回: SY-SUBRC 为 0 时则所有记录都成功更新。 SY-DBCNT 该值为成功更新的记录数。 4.10 For All Entries 语句语句 说明:For All Entries 后紧跟的内表称为驱动内表。 在使用该语句时,驱动内表不能为空,若为空,则等价于无此条件,可能造成全表的 检索。所以,在使用在使用 For All Entries 时,要先对驱动内表是否为空进行判断时,要先对驱动内表是否为空进行判断 在使用该语句前,尽可能保持少的驱动内表条目数,尽可能使驱动内表中的条目为唯 一的(可先进行删除重复行等操作) 。这样可避免在数据库中进行了不必要的读取 避免使用该语句读取小的数据库表(如配置表) ,对于小的数据库表使用单独的 SELECT 语句将会更好 使用该语句时,尽可能的连接数据库表中的索引,否则将会重复的全表扫描,并注意 按数据库表中的索引顺序来写 Where 语句 驱动内表与 INTO 后的内表不可以是同一个内表 4.11 Open SQL 与与 Native SQL 比较比较 开发人员不准使用 Native SQL,如果非得使用,请与项目组技术负责人确认。 4.12 表连接表连接 表连接取数是在数据库服务器里进行的,可以减少数据在网络上的传输。但是有一个缺点 是表连接不使用应用服务器的缓存。 例子例子 SELECT vbakauart vbapmatnr INTO TABLE it_tab FROM vbak AS vbak INNER JOIN vbap AS vbap ON vbakvbeln = vbapvbeln WHERE vbeln IN s_vbeln. vbak 为主表 vbap 为副表 在 where 条件中必须有主表的关键字或索引 在 where 条件中尽可能的指定跟主表有关的条件 在某种情况下可以考虑建立对应的视图来代替表连接的使用 5内表使用注意内表使用注意 5.1 内表定义内表定义 在定义内表时,建议先定义对应的类型,再定义内表。 TYPES: BEGIN OF ty_company, bukrs TYPE t001t-bukrs,“公司代码 txtna TYPE t001t-txtna, “公司代码描述 END OF ty_company. DATA: it_company TYPE STANDARD TABLE OF ty_company, wa_company TYPE ty_company. 注意:注意:请避免使用带表头的内表,请避免使用带表头的内表,SAP 将会废弃此荐功能,目前在面向对象编程中不支持带表将会废弃此荐功能,目前在面向对象编程中不支持带表 头的内表。头的内表。 5.2 内表使用内表使用 5.2.1修改内表中的字段值修改内表中的字段值 使用 MODIFY it_tab TRANSPORTING field . WHERE 将可加快修改内表记录集的速度。特别是在内表行中有嵌套内表时,该写法将可 明显增加速度。 通过 LOOP 处理来修改所有的组件 内表的大量修改必须通过在 LOOP 循环中来实现 5.2.2把一个内表附加到另一个内表后面把一个内表附加到另一个内表后面 例: 说明:内表 it_tab01 和内表 it_tab02 有相同的行结构且包含一定的记录集。 错误写法错误写法正确写法正确写法 LOOP AT it_tab01 INTO wa_tab. APPEND wa_tab TO it_tab02. ENDLOOP. APPEND LINES OF it_tab01 TO it_tab02. 通过 LOOP 循环实现内表的附加通过 APPEND LINES OF 语句批量附加内表 5.2.3删除内表中重复行删除内表中重复行 先对一个内表按删除时比较的字段进行排序,再通过以下语句删除重复行。 DELETE ADJACENT DUPLICATES FROM it_tab COMPARING field. 5.2.4根据条件删除内表中的行根据条件删除内表中的行 错误写法错误写法正确写法正确写法 LOOP AT it_tab INTO wa_tab. IF wa_tab-filed = value. DELETE it_tab INDEX sy-tabix. ENDIF. ENDLOOP. DELETE it_tab WHERE filed = value. 通过 LOOP 循环进行删除通过 WHERE 语句进行删除 5.2.5内表是否为空的判断内表是否为空的判断 错误写法错误写法正确写法正确写法 DESCRIBE TABLE it_tab LINES l_lines. IF l_lines = 0. * 内表为空 ENDIF. IF it_tab IS INITIAL. * 内表为空 ENDIF. 5.2.6读取内表行读取内表行 关键字读取 READ TABLE it_tab INTO wa_tab WITH KEY field = value. 关键字二分搜索读取 READ TABLE it_tab INTO wa_tab WITH KEY field = value BINARY SEARCH. 索引读取 R EAD TABLE it_tab INTO wa_tab WITH KEY field = value INDEX l_index. *注意:在使用二分搜索读取内表时,要先按关键字对内表进行排序注意:在使用二分搜索读取内表时,要先按关键字对内表进行排序 性能比较:性能比较: 类型类型性能比较性能比较 索引读取最快 关键字二分搜索读取较快 关键字读取慢 使用原则:使用原则: 当可以通过指定索引读取内表时,用指定索引读取 如若不能,则通过二分搜索进行读取 例: 不建议写法不建议写法 LOOP AT it_tab01 INTO wa_tab01. READ TABLE it_tab02 INTO wa_tab02 WITH KEY field = wa_tab01-field. CHECK sy-subrc = 0. ENDLOOP. 通过内表关键字来连接两个表 建议写法建议写法 SORT it_tab02 BY field. LOOP AT it_tab01 INTO wa_tab01. READ TABLE it_tab02 INTO wa_tab02 WITH KEY field = wa_tab01-field BINARY SEARCH. CHECK sy-subrc = 0. ENDLOOP. 通过使用关键字二分搜索来连接两个表 要对内表 it_tab02 先按字段 field 进行排序 5.2.7通过通过 LOOP AT it_tab ASSIGNING 循环内表循环内表 由于字符变量指向内存地址,此种方式比 LOOP AT it_tab INTO wa_tab 方式快 适合于数据量大、需要循环处理的内表 在 READ 语句中也可以使用类似的字符变量。 注意: 使用此种方式请确保对字符变量有充分理解 在此种方式的内表循环中,不要再使用 ASSIGN 语句给指定其它的值 通过此种方式修改内表行的数据时,不要使用 MODIFY 语句 5.2.8通过平行光标来连接两个内表通过平行光标来连接两个内表 方法一: 建议写法建议写法 SORT it_tab01 BY field. SORT it_tab02 BY field. l_index = 1. LOOP AT it_tab01 INTO wa_tab01. READ TABLE it_tab02 INTO wa_tab02 INDEX l_index. CHECK sy-subrc = 0. IF wa_tab02-field = wa_tab01-field. l_index = l_index + 1. ENDIF. ENDLOOP. 要先对内表 it_tab01 和 it_tab02 按字段 field 进行排序 当两个内表条目数很大时,如上 十万、百万,使用此种方法将会极大的增加程序性能 方法二: 不建议写法不建议写法 SORT it_tab01 BY field. SORT it_tab02 BY field. LOOP AT it_tab01 INTO wa_tab01. LOOP AT it_tab02 INTO wa_tab02 WHERE field = wa_tab01-field. . ENDLOOP. ENDLOOP. 通过 loop 循环中的 where 条件来进行连接,当内表条目数很大时,速度将会很慢,因为第 二个 loop 循环相当于在全表扫描的查找内表 it_tab02 建议写法建议写法 SORT it_tab01 BY field. SORT it_tab02 BY field. Comment austin1: 20120211 张三 删除 Comment austin2: 20120211 张三添 加 LOOP AT it_tab01 INTO wa_tab01. CLEAR l_tabix. READ TABLE it_tab02 INTO wa_tab02 WITH KEY field = wa_tab-field BINARY SEARCH. CHECK sy-subrc = 0. l_tabix
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 企业员工反恐培训制度
- 药品生产企业人事管理制度
- 《走进化学科学》参考课件
- 老年人常见病的家庭护理
- 调味品行业趋势分析与应对方案
- 高风险妊娠双向转诊协议书
- 风能领域员工绩效考核制度
- 临建房围挡施工方案的实施
- 高校中高层管理人才培养方案
- 电缆槽与其他设施协调施工方案
- 全国河流水文站坐标
- 高考专题复习:散句与整句变换(课件32张)
- 雾化吸入常见并发症的预防与处理
- 中小学幼儿园数字化教学资源进校园管理办法
- 高效课堂做好笔记 课件-学习习惯的培养主题班会
- 鞍钢鲅鱼圈钢铁基地项目设计方案
- 消化内镜清洗消毒技术操作流程图(最新)
- 人卫版外科学小肠疾病第一、二、三节课件
- 《区块链应用技术》课程教学大纲
- 工程变更洽商记录样板
- 内蒙古蒙特威生物科技有限公司3000吨酪蛋白及衍生产品项目环评报告表
评论
0/150
提交评论