假设我创建了一个类ZCL_WORKORDER_GENERIC
,其中包含一个属性,即ZCL_OPERATIONS_GENERIC
对象的表。
现在我想创建一个从第一个继承的新类ZCL_WORKORDER_SPECIFIC1
,但操作列表也应该用类ZCL_OPERATIONS_SPECIFIC1
覆盖。
在ABAP中实现这一目标的最佳方法是什么?
通常不能覆盖属性,这是ABAP OO模型所禁止的。
但是,您可以执行以下技巧:将超类的属性声明为类型DATA
(也称为数据对象(,然后在扩展超类的子类中为其分配所需的任何类型。
假设您有两种类型的操作,它们唯一标识自身:zcl_operations_generic:
CLASS zcl_operations_generic DEFINITION.
PUBLIC SECTION.
DATA l_wheel TYPE char5 VALUE 'wheel'.
METHODS: identify_me.
ENDCLASS.
CLASS zcl_operations_generic IMPLEMENTATION.
METHOD identify_me.
WRITE: `I am `, condense( cl_abap_classdescr=>get_class_name( me ) ), `and I have a`, l_wheel.
ENDMETHOD.
ENDCLASS.
和更具体的zcl_operations_specific1
CLASS zcl_operations_specific1 DEFINITION.
PUBLIC SECTION.
DATA l_wheel TYPE char10 VALUE 'truckwheel'.
METHODS: who_am_i.
ENDCLASS.
CLASS zcl_operations_specific1 IMPLEMENTATION.
METHOD who_am_i.
WRITE: `I am `, condense( cl_abap_classdescr=>get_class_name( me ) ), `and I have a`, l_wheel.
ENDMETHOD.
ENDCLASS.
然后,在创建泛型工作订单zcl_workorder_generic时,将操作mr_data
指定为泛型类型,并在构造函数和方法中执行必要的类型初始化:
CLASS zcl_workorder_generic DEFINITION.
PUBLIC SECTION.
DATA mr_data TYPE REF TO data.
METHODS initialize EXPORTING out_order TYPE ANY.
METHODS constructor.
ENDCLASS.
CLASS zcl_workorder_generic IMPLEMENTATION.
METHOD initialize.
FIELD-SYMBOLS: <lr_data> TYPE REF TO zcl_operations_generic.
ASSIGN mr_data->* TO <lr_data>.
CREATE OBJECT <lr_data>.
out_order = <lr_data>.
ENDMETHOD.
METHOD constructor.
CREATE DATA mr_data TYPE REF TO zcl_operations_generic.
ENDMETHOD.
ENDCLASS.
在特定的工作顺序zcl_workorder_specific1中,您应该为新类型重新定义构造函数/初始化方法:
CLASS zcl_workorder_specific1 DEFINITION INHERITING FROM zcl_workorder_generic.
PUBLIC SECTION.
METHODS initialize REDEFINITION.
METHODS constructor.
ENDCLASS.
CLASS zcl_workorder_specific1 IMPLEMENTATION.
METHOD initialize.
FIELD-SYMBOLS: <lr_data> TYPE REF TO zcl_operations_specific1.
ASSIGN mr_data->* TO <lr_data>.
CREATE OBJECT <lr_data>.
out_order = <lr_data>.
ENDMETHOD.
METHOD constructor.
super->constructor( ).
CREATE DATA mr_data TYPE REF TO zcl_operations_specific1.
ENDMETHOD.
ENDCLASS.
然后你可以像这样实例化你的订单,然后通过调用相应的属性方法来检查属性是否正确实例化:
START-OF-SELECTION.
DATA: generic TYPE REF TO zcl_operations_generic.
DATA(lr_work_order) = NEW zcl_workorder_generic( ).
lr_work_order->initialize( IMPORTING out_order = generic ).
generic->identify_me( ).
WRITE: /.
DATA: specific TYPE REF TO zcl_operations_specific1.
lr_work_order = NEW zcl_workorder_specific1( ).
lr_work_order->initialize( IMPORTING out_order = specific ).
specific->who_am_i( ).
但这只是一个棘手的解决方法,我同意弗洛里安覆盖属性是一种不应该使用的糟糕方法。
假设你有
CLASS zcl_workorder_generic DEFINITION.
PROTECTED SECTION.
DATA operations TYPE REF TO zcl_operations_generic.
ENDCLASS.
然后你可以很容易地推导出
CLASS zcl_workorder_specific1 DEFINITION INHERITING FROM zcl_workorder_generic.
ENDCLASS
并且该子类自然可以访问operations
,键入为zcl_operations_generic
。
然后,您的特定操作可以是
CLASS zcl_operations_specific1 DEFINITION INHERITING FROM zcl_operations_generic.
ENDCLASS.
在面向对象设计中,zcl_operations_specific1
必须是zcl_operations_generic
的真正子类。如果你觉得需要添加/更改不属于~generic
的~specific1
方法,你的面向对象设计是错误的,因为它违反了Liskov替换原则。"错误"意味着你仍然可以让它工作,不知何故,有很多CAST,但你会反对你的设计,而不是让自己被它所困扰。
无论如何,您应该添加接口。继承是在实现之间共享代码的一种方式。接口声明类之间的协定,并为您提供更多的独立性,例如在编写单元测试时。