ABAP核心进阶篇(120篇):类与对象基础概念(10篇)
第八篇:类的组件可见性优化:如何通过属性私有化提升代码健壮性
博客标题:《类的组件可见性优化:如何通过属性私有化提升代码健壮性》
博客简介:结合业务开发场景,讲解"属性私有化+公开GET/SET方法"的设计思路,演示如何通过方法实现属性的合法性校验、变更日志记录等附加逻辑,解决属性直接修改导致的数据不一致问题。
📖 写在前面
组件可见性是面向对象编程中实现封装性的核心机制。在ABAP OOP中,类的组件(属性和方法)通过访问控制符(PUBLIC、PRIVATE、PROTECTED)控制其可见性。
属性私有化是封装性的重要体现。通过将属性设置为PRIVATE或PROTECTED,并提供公开的GET/SET方法,可以实现:
| 附加能力 | 说明 |
|---|---|
| ✅合法性校验 | 在SET方法中验证数据有效性 |
| ✅变更日志记录 | 自动记录属性修改历史 |
| ✅数据一致性 | 保证业务规则不被绕过 |
| ✅自动计算 | 修改时自动更新关联属性 |
本文将结合多个业务场景,讲解"属性私有化+公开GET/SET方法"的设计思路,并通过完整代码示例演示如何落地。
一、组件可见性概述
1.1 访问控制符的作用范围
1.2 对比速查
| 维度 | PUBLIC | PRIVATE | PROTECTED |
|---|---|---|---|
| 类内部访问 | ✅ | ✅ | ✅ |
| 外部访问 | ✅ | ❌ | ❌ |
| 子类访问 | ✅ | ❌ | ✅ |
| 封装性 | 🔴 低 | 🟢最高 | 🟡 中 |
| 适用场景 | 对外接口 | 内部实现 | 继承接口 |
二、属性私有化的设计模式
2.1 核心设计思路
2.2 属性私有化的优点
| 优点 | 说明 |
|---|---|
| 封装性 | 隐藏内部实现,只暴露必要的接口 |
| 数据安全 | 通过验证逻辑保证数据的合法性 |
| 数据一致性 | 避免属性直接修改导致的不一致 |
| 可维护性 | 集中管理属性的访问和修改逻辑 |
| 可扩展性 | 方便添加附加逻辑(日志、审计等) |
三、GET/SET方法设计规范
3.1 方法命名规范
| 方法类型 | 命名格式 | 示例 |
|---|---|---|
| GET方法 | get_+ 属性名称 | get_amount、get_status |
| SET方法 | set_+ 属性名称 | set_amount、set_status |
3.2 GET方法设计规范
" ✅ 标准 GET 方法模板 METHOD get_amount. rv_amount = amount. " 简单返回属性值 ENDMETHOD. " 无副作用,轻量级| 规范 | 说明 |
|---|---|
| 单一职责 | 只获取属性值,不做其他操作 |
| 无副作用 | 不修改任何对象状态 |
| 轻量级 | 性能开销应尽可能小 |
3.3 SET方法设计规范
" ✅ 标准 SET 方法模板 METHOD set_amount. " ① 参数验证 IF iv_amount <= 0. RAISE invalid_amount. ENDIF. " ② 记录旧值(用于日志) DATA(lv_old) = CONV string( amount ). " ③ 修改属性 amount = iv_amount. " ④ 附加逻辑(日志/审计) log_change( iv_field = 'AMOUNT' iv_old = lv_old iv_new = CONV string( amount ) ). ENDMETHOD.| 规范 | 说明 |
|---|---|
| 参数验证 | 必须验证参数的合法性 |
| 异常处理 | 验证失败时抛出异常 |
| 附加逻辑 | 可包含日志、审计等 |
| 数据一致性 | 保证业务规则不被破坏 |
四、实战场景一:订单金额验证
4.1 业务需求
| 需求项 | 说明 |
|---|---|
| 📌 金额必须大于0 | 负数或零值不允许 |
| 📌 金额不能超过100万 | 超限需提示 |
| 📌 变更需记录日志 | 便于审计追溯 |
4.2 完整实现
" ================================================================ " 场景:订单金额验证 + 变更日志 " ================================================================ CLASS zcl_mm_order DEFINITION. PUBLIC SECTION. METHODS constructor. METHODS set_amount IMPORTING iv_amount TYPE netwr EXCEPTIONS invalid_amount. METHODS get_amount RETURNING VALUE(rv_amount) TYPE netwr. METHODS display_order. PRIVATE SECTION. DATA: order_id TYPE ebeln VALUE '4500000001'. DATA: amount TYPE netwr. DATA: status TYPE char2 VALUE '00'. CONSTANTS: gc_max_amount TYPE netwr VALUE 1000000. METHODS validate_amount IMPORTING iv_amount TYPE netwr RETURNING VALUE(rv_valid) TYPE abap_bool. METHODS log_change IMPORTING iv_field TYPE char30 iv_old_value TYPE string iv_new_value TYPE string. ENDCLASS. CLASS zcl_mm_order IMPLEMENTATION. METHOD constructor. amount = 0. ENDMETHOD. METHOD set_amount. " ✅ 1. 验证 IF validate_amount( iv_amount ) = abap_false. RAISE invalid_amount. ENDIF. " ✅ 2. 记录旧值 DATA(lv_old) = CONV string( amount ). " ✅ 3. 修改 amount = iv_amount. " ✅ 4. 记录日志 log_change( iv_field = 'AMOUNT' iv_old_value = lv_old iv_new_value = CONV string( amount ) ). WRITE: / '✅ 金额设置成功:', amount. ENDMETHOD. METHOD get_amount. rv_amount = amount. ENDMETHOD. METHOD validate_amount. rv_valid = abap_true. IF iv_amount <= 0. rv_valid = abap_false. WRITE: / '❌ 金额必须大于0'. ELSEIF iv_amount > gc_max_amount. rv_valid = abap_false. WRITE: / '❌ 金额超过最大限制:', gc_max_amount. ENDIF. ENDMETHOD. METHOD log_change. WRITE: / '📋 变更日志:'. WRITE: / ' 字段:', iv_field. WRITE: / ' 原值:', iv_old_value. WRITE: / ' 新值:', iv_new_value. WRITE: / ' 时间:', sy-datum, sy-uzeit. ENDMETHOD. METHOD display_order. WRITE: / '📄 订单信息:'. WRITE: / ' 订单号:', order_id. WRITE: / ' 金额:', amount. WRITE: / ' 状态:', status. ENDMETHOD. ENDCLASS. " ✅ 使用示例 START-OF-SELECTION. DATA(lo_order) = NEW zcl_mm_order( ). TRY. lo_order->set_amount( 15000 ). " ✅ 成功 lo_order->display_order( ). lo_order->set_amount( -1000 ). " ❌ 触发异常 CATCH cx_root INTO DATA(lo_ex). WRITE: / '❌ 错误:', lo_ex->get_text( ). ENDTRY.五、实战场景二:订单状态流转控制
5.1 业务需求
状态转换规则: '00' → '01' → '02' → '03' → '04' 不允许跳跃或回退5.2 状态机实现
" ================================================================ " 场景:订单状态流转控制(状态机模式) " ================================================================ CLASS zcl_mm_order DEFINITION. PUBLIC SECTION. METHODS constructor. METHODS set_status IMPORTING iv_status TYPE char2 EXCEPTIONS invalid_status status_transition_error. METHODS get_status RETURNING VALUE(rv_status) TYPE char2. PRIVATE SECTION. DATA: order_id TYPE ebeln VALUE '4500000001'. DATA: status TYPE char2. METHODS validate_status_transition IMPORTING iv_new_status TYPE char2 RETURNING VALUE(rv_valid) TYPE abap_bool. ENDCLASS. CLASS zcl_mm_order IMPLEMENTATION. METHOD constructor. status = '00'. ENDMETHOD. METHOD set_status. " ✅ 验证状态转换 IF validate_status_transition( iv_status ) = abap_false. RAISE status_transition_error. ENDIF. status = iv_status. WRITE: / '✅ 状态转换成功:', status. ENDMETHOD. METHOD validate_status_transition. rv_valid = abap_true. CASE status. WHEN '00'. IF iv_new_status <> '01'. rv_valid = abap_false. ENDIF. WHEN '01'. IF iv_new_status NOT IN ('02', '04'). rv_valid = abap_false. ENDIF. WHEN '02'. IF iv_new_status NOT IN ('03', '04'). rv_valid = abap_false. ENDIF. WHEN '03'. IF iv_new_status <> '04'. rv_valid = abap_false. ENDIF. WHEN '04'. rv_valid = abap_false. WRITE: / '❌ 状态04为终态,不可再转换'. ENDCASE. IF rv_valid = abap_false. WRITE: / '❌ 状态转换错误: 当前状态', status, '→ 目标状态', iv_new_status, '不允许'. ENDIF. ENDMETHOD. METHOD get_status. rv_status = status. ENDMETHOD. ENDCLASS. " ✅ 使用示例 START-OF-SELECTION. DATA(lo_order) = NEW zcl_mm_order( ). TRY. lo_order->set_status( '01' ). " ✅ 00→01 允许 lo_order->set_status( '02' ). " ✅ 01→02 允许 lo_order->set_status( '03' ). " ✅ 02→03 允许 lo_order->set_status( '04' ). " ✅ 03→04 允许 lo_order->set_status( '01' ). " ❌ 04→01 不允许 CATCH cx_root INTO DATA(lo_ex). WRITE: / '❌ 错误:', lo_ex->get_text( ). ENDTRY.六、属性公开 vs 属性私有化对比
6.1 代码对比
| 对比维度 | ❌ 属性公开(PUBLIC) | ✅ 属性私有化(PRIVATE) |
|---|---|---|
| 访问方式 | 直接访问lo_order->amount | 通过方法set_amount() |
| 验证逻辑 | ❌ 无验证 | ✅ 完整验证 |
| 数据安全 | 🔴 可随意赋值 | 🟢 受控修改 |
| 日志记录 | ❌ 无法自动记录 | ✅ 自动记录 |
6.2 错误 vs 正确示例
" ❌ 错误做法:属性公开,外部可直接修改 CLASS zcl_bad_order DEFINITION. PUBLIC SECTION. DATA: amount TYPE netwr. " ❌ 公开属性 DATA: status TYPE char2. " ❌ 公开属性 ENDCLASS. DATA(lo_bad) = NEW zcl_bad_order( ). lo_bad->amount = -1000. " ❌ 负数未验证 lo_bad->status = '99'. " ❌ 无效状态" ✅ 正确做法:属性私有化,通过方法访问 CLASS zcl_good_order DEFINITION. PUBLIC SECTION. METHODS set_amount IMPORTING iv_amount TYPE netwr EXCEPTIONS invalid_amount. METHODS get_amount RETURNING VALUE(rv_amount) TYPE netwr. PRIVATE SECTION. DATA: amount TYPE netwr. " ✅ 私有属性 ENDCLASS. DATA(lo_good) = NEW zcl_good_order( ). TRY. lo_good->set_amount( 15000 ). " ✅ 通过方法,有验证 CATCH cx_root. ENDTRY.七、属性私有化设计模式总结
7.1 三种常见模式
| 模式 | 适用场景 | 核心特征 |
|---|---|---|
| GET/SET模式 | 需要验证和日志 | 属性私有 + GET/SET方法 |
| 只读属性模式 | 不可变数据 | READ-ONLY+ 仅GET方法 |
| 状态机模式 | 状态流转控制 | 私有状态 + 转换验证 |
7.2 设计原则
| 原则 | 说明 |
|---|---|
| 优先私有化 | 默认将属性设为PRIVATE,仅在必要时开放 |
| 最小权限 | 只赋予必要的最小访问权限 |
| 验证前置 | 所有数据修改必须在SET方法中验证 |
| 日志完备 | 关键属性的变更应记录日志 |
八、快速参考卡片
属性私有化检查清单
| 检查项 | 状态 |
|---|---|
属性是否设置为PRIVATE或PROTECTED? | □ |
是否提供了GET方法获取属性值? | □ |
GET方法是否无副作用、轻量级? | □ |
是否提供了SET方法设置属性值? | □ |
SET方法是否包含完整的验证逻辑? | □ |
SET方法是否记录了变更日志? | □ |
是否处理了异常情况(EXCEPTIONS)? | □ |
九、总结
| 核心要点 | 说明 |
|---|---|
| 属性私有化 | 将属性设为PRIVATE,不对外暴露 |
| GET方法 | 轻量级、无副作用 |
| SET方法 | 验证 → 修改 → 附加逻辑 |
| 数据安全 | 通过验证保证数据合法性 |
| 数据一致性 | 集中管理,统一控制 |
下一篇预告:《面向对象与过程化编程的对比分析:适用场景与选型决策》
作者:爱喝水的鱼丶
版本记录:2026年6月
💬你在实际项目中如何设计类的属性可见性?遇到过哪些属性直接修改导致的问题?欢迎在评论区分享你的经验!