Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
Michael_Keller
Active Contributor
Dear community, I'm always looking for easy to understand examples of how to do certain things.That helps me with my understanding and it helps me when I want to explain it to students and trainees, for example. I recently looked at the subject of "dependency injection" again. In that context, I thought about a nice example from real life. The result is an example with "fire".

In order for a fire to arise or exist, certain requirements must be met. There is a nice figure about this on Wikipedia. As shown in the figure, three factors are important:

  1. fuel

  2. oxygen

  3. heat


If any of these factors are absent or if one of these factors is unbalanced, there is no fire. You learn that at an early age as a firefighter. So here we have some real dependencies 🙂

Ok, then we want to map that example to ABAP classes. Let's start.

First of all there is a class ZCL_FUEL, which uses its attribute MV_FLAMMABLE to indicate whether a substance is flammable.
CLASS zcl_fuel DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.

PUBLIC SECTION.
DATA mv_flammable TYPE abap_bool READ-ONLY.

METHODS constructor
IMPORTING
iv_flammable TYPE abap_bool.
ENDCLASS.

CLASS zcl_fuel IMPLEMENTATION.
METHOD constructor.
mv_flammable = iv_flammable.
ENDMETHOD.
ENDCLASS.

The next class is ZCL_OXYGEN, which uses its attribute MV_SUFFICIENT_QUANTITY to indicate whether there is enough oxygen for a fire.
CLASS zcl_oxygen DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.

PUBLIC SECTION.
DATA mv_sufficient_quantity TYPE abap_bool READ-ONLY.

METHODS constructor
IMPORTING
iv_sufficient_quantity TYPE abap_bool.
ENDCLASS.

CLASS zcl_oxygen IMPLEMENTATION.
METHOD constructor.
mv_sufficient_quantity = iv_sufficient_quantity.
ENDMETHOD.
ENDCLASS.

The next class is ZCL_HEAT. Sufficient heat is required for a fire to start or exist.
CLASS zcl_heat DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.

PUBLIC SECTION.
DATA mv_sufficient_heat TYPE abap_bool READ-ONLY.

METHODS constructor
IMPORTING
iv_sufficient_heat TYPE abap_bool.
ENDCLASS.

CLASS zcl_heat IMPLEMENTATION.
METHOD constructor.
mv_sufficient_heat = iv_sufficient_heat.
ENDMETHOD.
ENDCLASS.

The three classes mentioned above play a role for the ZCL_FIRE class. This class can be used to illustrate a "constructor injection" and a "setter injection". In practice, one would only use constructor injection. According to Clean ABAP, setter injection isn't recommended. Regardless, that's how it should be for the example 😉
CLASS zcl_fire DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.

PUBLIC SECTION.
METHODS constructor
IMPORTING
io_fuel TYPE REF TO zcl_fuel OPTIONAL
io_oxygen TYPE REF TO zcl_oxygen OPTIONAL
io_heat TYPE REF TO zcl_heat OPTIONAL.

METHODS set_fuel
IMPORTING
io_fuel TYPE REF TO zcl_fuel.

METHODS set_oxygen
IMPORTING
io_oxygen TYPE REF TO zcl_oxygen.

METHODS set_head
IMPORTING
io_heat TYPE REF TO zcl_heat.

METHODS is_burning
RETURNING VALUE(rv_result) TYPE abap_bool.

PRIVATE SECTION.
DATA mo_fuel TYPE REF TO zcl_fuel.
DATA mo_oxygen TYPE REF TO zcl_oxygen.
DATA mo_heat TYPE REF TO zcl_heat.
ENDCLASS.

CLASS zcl_fire IMPLEMENTATION.
METHOD constructor.
mo_fuel = io_fuel.
mo_oxygen = io_oxygen.
mo_heat = io_heat.
ENDMETHOD.

METHOD is_burning.
IF mo_fuel->mv_flammable = abap_true AND
mo_oxygen->mv_sufficient_quantity = abap_true AND
mo_heat->mv_sufficient_heat = abap_true.
rv_result = abap_true.
ENDIF.
ENDMETHOD.

METHOD set_fuel.
mo_fuel = io_fuel.
ENDMETHOD.

METHOD set_head.
mo_heat = io_heat.
ENDMETHOD.

METHOD set_oxygen.
mo_oxygen = io_oxygen.
ENDMETHOD.
ENDCLASS.

Finally, a couple of ABAP unit tests. The fire only burns if the three factors fuel, oxygen and heat with appropriate properties or in sufficient quantities are available. The prefix "CI" stands for "constructor injection" and the prefix "SI" stands for "setter injection".
CLASS ltc_fire DEFINITION FOR TESTING DURATION LONG RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS ci_fire_when_everything_fits FOR TESTING.
METHODS ci_no_fire_not_enough_heat FOR TESTING.
METHODS si_fire_when_everything_fits FOR TESTING.
METHODS si_no_fire_not_enough_heat FOR TESTING.
ENDCLASS.

CLASS ltc_fire IMPLEMENTATION.
METHOD ci_fire_when_everything_fits.
DATA(lo_fuel) = NEW zcl_fuel( iv_flammable = abap_true ).
DATA(lo_oxygen) = NEW zcl_oxygen( iv_sufficient_quantity = abap_true ).
DATA(lo_heat) = NEW zcl_heat( iv_sufficient_heat = abap_true ).

DATA(lo_fire) = NEW zcl_fire( io_fuel = lo_fuel
io_oxygen = lo_oxygen
io_heat = lo_heat ).

DATA(lv_is_burning) = lo_fire->is_burning( ).

cl_abap_unit_assert=>assert_true( act = lv_is_burning ).
ENDMETHOD.

METHOD ci_no_fire_not_enough_heat.
DATA(lo_fuel) = NEW zcl_fuel( iv_flammable = abap_true ).
DATA(lo_oxygen) = NEW zcl_oxygen( iv_sufficient_quantity = abap_true ).
DATA(lo_heat) = NEW zcl_heat( iv_sufficient_heat = abap_false ).

DATA(lo_fire) = NEW zcl_fire( io_fuel = lo_fuel
io_oxygen = lo_oxygen
io_heat = lo_heat ).

DATA(lv_is_burning) = lo_fire->is_burning( ).

cl_abap_unit_assert=>assert_false( act = lv_is_burning ).
ENDMETHOD.

METHOD si_fire_when_everything_fits.
DATA(lo_fuel) = NEW zcl_fuel( iv_flammable = abap_true ).
DATA(lo_oxygen) = NEW zcl_oxygen( iv_sufficient_quantity = abap_true ).
DATA(lo_heat) = NEW zcl_heat( iv_sufficient_heat = abap_true ).

DATA(lo_fire) = NEW zcl_fire( ).
lo_fire->set_fuel( lo_fuel ).
lo_fire->set_head( lo_heat ).
lo_fire->set_oxygen( lo_oxygen ).

DATA(lv_is_burning) = lo_fire->is_burning( ).

cl_abap_unit_assert=>assert_true( act = lv_is_burning ).
ENDMETHOD.

METHOD si_no_fire_not_enough_heat.
DATA(lo_fuel) = NEW zcl_fuel( iv_flammable = abap_true ).
DATA(lo_oxygen) = NEW zcl_oxygen( iv_sufficient_quantity = abap_true ).
DATA(lo_heat) = NEW zcl_heat( iv_sufficient_heat = abap_false ).

DATA(lo_fire) = NEW zcl_fire( ).
lo_fire->set_fuel( lo_fuel ).
lo_fire->set_head( lo_heat ).
lo_fire->set_oxygen( lo_oxygen ).

DATA(lv_is_burning) = lo_fire->is_burning( ).

cl_abap_unit_assert=>assert_false( act = lv_is_burning ).
ENDMETHOD.
ENDCLASS.

That's my example on the subject of "dependency injection". Now some questions to the community:

  1. What do you think about the example? Is it easy to understand?

  2. Can you recommend a better one?

  3. Is it sufficient to implement the "dependency inversion principle" by changing the IMPORTING parameters of the constructor to use interfaces?


 

Best regards, thanks for reading and please stay healthy

Michael

 


P.S.: Please support the virtual wishing well.

P.S.S.: Not tired of reading blogs? Check this blog by jrg.krause.

10 Comments
Labels in this area