ABAP设计模式之---“Tell, Don’t Ask原则”

发布于:2025-06-13 ⋅ 阅读:(86) ⋅ 点赞:(0)

“Tell, Don’t Ask”是一种重要的面向对象编程设计原则,它强调的是对象之间如何有效地交流和协作。


1. 什么是 Tell, Don’t Ask 原则?

这个原则的核心思想是:

“告诉一个对象该做什么,而不是询问一个对象的状态再对它作出决策。”

在这里插入图片描述

解释:
  • 问(Ask)”:当你通过直接访问某个对象的属性或调用其 getter 方法获取内部状态后,在方法外部进行逻辑处理。这实际上违反了封装性,把对象的责任分散到了外部。
  • 告诉(Tell)”:让对象本身承担它的责任,而不是让调用方替它做决定。换句话说,当需要某个功能时,直接调用对象的方法,让对象自己处理。

这种方式更符合面向对象编程的精神,使得代码更加模块化、责任分明。


2. 为什么要采用 Tell, Don’t Ask 原则?

  1. 增强封装性: 对象内部的数据和逻辑是一个整体,外部调用者不应该需要关心对象的细节。
  2. 减少耦合: 外部调用者如果不需要直接操作对象的内部状态,模块之间的依赖就会减少。
  3. 更易维护: 如果需求变更,只需修改对象内部逻辑,而外部调用者无需更改。
  4. 支持扩展: 遵循该原则的代码更容易新增功能,而不会影响现有代码。

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.
存在的问题:
  1. 封装性差: get_stock 方法暴露了产品对象的内部状态(库存 mv_stock)。外部调用者需要根据这些数据做逻辑判断,这让调用者替对象承担了责任。
  2. 耦合性高: 如果库存判断逻辑发生变化(比如要增加某些额外的检查条件),需要修改调用者的代码,违反了面向对象“封装变化”的原则。

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 ).

改进点分析:
  1. 封装性更强:
    • 库存数据 mv_stock 只在 zcl_product 类内部操作,不对外暴露。
    • 调用者不需要知道股票的具体值和具体检查逻辑,调用者只负责“告诉”产品要执行的动作。
  2. 低耦合:
    • 如果需要修改库存检查逻辑(比如加入更多条件),只需要修改 zcl_product->reduce_stock 的实现,不用改任何调用它的代码。
  3. 代码更简洁:
    • 主程序逻辑减少了很多判断,只需要调用方法 “告诉” 产品执行相应操作即可。

5. 更复杂的示例:通过 Tell, Don’t Ask 实现对象间协作

在实际业务中,多个对象可能需要协作。以下示例使用 “订单”和“产品”对象,在创建订单时通知产品检查并扣减库存。

代码示例(产品协作订单):
CLASS zcl_product DEFINITION.
  PUBLIC SECTION.
    METHODS: check_and_reduce_stock IMPORTING iv_quantity TYPE i
                                    RETURNING 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_product
                                   iv_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 的应用:
  1. 产品自身负责库存检查和扣减(Tell)。
    • 调用方 zcl_order 不需要关心产品内部如何检查库存。
  2. 订单类通过与产品协作完成订单创建。
    • 在订单处理过程中,直接“告诉”产品所需的操作,而不是获取产品状态后再做判断。

6. 总结

  • Tell, Don’t Ask 原则强化了面向对象中的封装性和职责分离。
  • 它让对象自己对自己的数据负责,调用者只需要告诉对象它想要完成什么工作。
  • 好处:降低耦合、增强扩展性,并使代码更易维护、更符合业务逻辑。

网站公告

今日签到

点亮在社区的每一天
去签到