ABAP设计模式之---“Tell, Don’t Ask原则”
“Tell, Don’t Ask”
是一种重要的面向对象编程设计原则,它强调的是对象之间如何有效地交流和协作。
1. 什么是 Tell, Don’t Ask 原则?
这个原则的核心思想是:
“告诉一个对象该做什么,而不是询问一个对象的状态再对它作出决策。”
解释:
- “问(Ask)”:当你通过直接访问某个对象的属性或调用其 getter 方法获取内部状态后,在方法外部进行逻辑处理。这实际上违反了封装性,把对象的责任分散到了外部。
- “告诉(Tell)”:让对象本身承担它的责任,而不是让调用方替它做决定。换句话说,当需要某个功能时,直接调用对象的方法,让对象自己处理。
这种方式更符合面向对象编程的精神,使得代码更加模块化、责任分明。
2. 为什么要采用 Tell, Don’t Ask 原则?
- 增强封装性: 对象内部的数据和逻辑是一个整体,外部调用者不应该需要关心对象的细节。
- 减少耦合: 外部调用者如果不需要直接操作对象的内部状态,模块之间的依赖就会减少。
- 更易维护: 如果需求变更,只需修改对象内部逻辑,而外部调用者无需更改。
- 支持扩展: 遵循该原则的代码更容易新增功能,而不会影响现有代码。
3. 不遵循 Tell, Don’t Ask 的代码示例
我们来看一个简单的例子:一个“订单”对象,负责检查产品库存是否充足,并扣减库存。如果不遵循 Tell, Don’t Ask 原则,调用者会直接查询库存,并负责判断库存是否足够,然后再调用库存扣减操作。
反例:
CLASS zcl_product DEFINITION.PUBLIC SECTION.METHODS: get_stock RETURNING VALUE(rv_stock) TYPE i,reduce_stock IMPORTING iv_quantity TYPE i.PRIVATE SECTION.DATA: mv_stock TYPE i VALUE 100. " 假设库存为 100
ENDCLASS.CLASS zcl_product IMPLEMENTATION.METHOD get_stock.rv_stock = mv_stock.ENDMETHOD.METHOD reduce_stock.mv_stock = mv_stock - iv_quantity.WRITE: / 'Stock reduced successfully. Remaining stock:', mv_stock.ENDMETHOD.
ENDCLASS." 主程序逻辑
START-OF-SELECTION.DATA(lo_product) = NEW zcl_product( ).DATA(lv_stock) TYPE i." 获取库存并检查lv_stock = lo_product->get_stock( ).IF lv_stock >= 50. " 外部负责判断库存是否足够lo_product->reduce_stock( iv_quantity = 50 ).ELSE.WRITE: / 'Not enough stock to reduce.'.ENDIF.
存在的问题:
- 封装性差:
get_stock
方法暴露了产品对象的内部状态(库存mv_stock
)。外部调用者需要根据这些数据做逻辑判断,这让调用者替对象承担了责任。 - 耦合性高: 如果库存判断逻辑发生变化(比如要增加某些额外的检查条件),需要修改调用者的代码,违反了面向对象“封装变化”的原则。
4. 遵循 Tell, Don’t Ask 的代码示例
根据 Tell, Don’t Ask 原则,我们让“产品”自己负责完成“库存检查 + 扣减库存”的逻辑,调用者只需要告诉产品“我需要从你这里减少多少库存”,具体判断过程由产品自己完成。
合规代码:
CLASS zcl_product DEFINITION.PUBLIC SECTION.METHODS: reduce_stock IMPORTING iv_quantity TYPE i.PRIVATE SECTION.DATA: mv_stock TYPE i VALUE 100. " 假设库存为 100
ENDCLASS.CLASS zcl_product IMPLEMENTATION.METHOD reduce_stock.IF mv_stock >= iv_quantity.mv_stock = mv_stock - iv_quantity.WRITE: / 'Stock reduced successfully. Remaining stock:', mv_stock.ELSE.WRITE: / 'Stock not sufficient.'.ENDIF.ENDMETHOD.
ENDCLASS." 主程序逻辑
START-OF-SELECTION.DATA(lo_product) = NEW zcl_product( )." 只需要告诉产品需要减少的库存数量lo_product->reduce_stock( iv_quantity = 50 ).
改进点分析:
- 封装性更强:
- 库存数据
mv_stock
只在zcl_product
类内部操作,不对外暴露。 - 调用者不需要知道股票的具体值和具体检查逻辑,调用者只负责“告诉”产品要执行的动作。
- 库存数据
- 低耦合:
- 如果需要修改库存检查逻辑(比如加入更多条件),只需要修改
zcl_product->reduce_stock
的实现,不用改任何调用它的代码。
- 如果需要修改库存检查逻辑(比如加入更多条件),只需要修改
- 代码更简洁:
- 主程序逻辑减少了很多判断,只需要调用方法 “告诉” 产品执行相应操作即可。
5. 更复杂的示例:通过 Tell, Don’t Ask 实现对象间协作
在实际业务中,多个对象可能需要协作。以下示例使用 “订单”和“产品”对象,在创建订单时通知产品检查并扣减库存。
代码示例(产品协作订单):
CLASS zcl_product DEFINITION.PUBLIC SECTION.METHODS: check_and_reduce_stock IMPORTING iv_quantity TYPE iRETURNING VALUE(rv_success) TYPE abap_bool.PRIVATE SECTION.DATA: mv_stock TYPE i VALUE 100.
ENDCLASS.CLASS zcl_product IMPLEMENTATION.METHOD check_and_reduce_stock.IF mv_stock >= iv_quantity.mv_stock = mv_stock - iv_quantity.WRITE: / 'Stock reduced successfully. Remaining stock:', mv_stock.rv_success = abap_true.ELSE.WRITE: / 'Stock not sufficient.'.rv_success = abap_false.ENDIF.ENDMETHOD.
ENDCLASS." 订单类
CLASS zcl_order DEFINITION.PUBLIC SECTION.METHODS: create_order IMPORTING io_product TYPE REF TO zcl_productiv_quantity TYPE i.
ENDCLASS.CLASS zcl_order IMPLEMENTATION.METHOD create_order.DATA: lv_success TYPE abap_bool." 告诉产品检查和减少库存lv_success = io_product->check_and_reduce_stock( iv_quantity = iv_quantity )." 根据结果输出订单创建情况IF lv_success = abap_true.WRITE: / 'Order created successfully.'.ELSE.WRITE: / 'Order creation failed due to insufficient stock.'.ENDIF.ENDMETHOD.
ENDCLASS." 主程序逻辑
START-OF-SELECTION.DATA(lo_product) = NEW zcl_product( ).DATA(lo_order) = NEW zcl_order( )." 创建一个订单,直接告诉产品该怎么做lo_order->create_order( io_product = lo_product iv_quantity = 50 ).
关键点:Tell, Don’t Ask 的应用:
- 产品自身负责库存检查和扣减(Tell)。
- 调用方
zcl_order
不需要关心产品内部如何检查库存。
- 调用方
- 订单类通过与产品协作完成订单创建。
- 在订单处理过程中,直接“告诉”产品所需的操作,而不是获取产品状态后再做判断。
6. 总结
- Tell, Don’t Ask 原则强化了面向对象中的封装性和职责分离。
- 它让
对象自己对自己的数据负责
,调用者只需要告诉对象
它想要完成什么工作。 - 好处:降低耦合、增强扩展性,并使代码更易维护、更符合业务逻辑。