IoC implemented

Challenge

Yesterday we had an interesting discussion about the following requirement: There is an application which consists of a webdynpro UI and a backend model. The backend model is described by an interface ZIF_SOME_BACKEND_MODEL which is implemented by some specific classes ZCL_BACKEND_MODEL_A and ZCL_BACKEND_MODEL_B. Backend A and Backend B are performing their tasks quite differently, however, the UI does not really care about this fact, as it uses only the interface ZIF_SOME_BACKEND_MODEL.
IoC implemented - Initial situation

Model A and B may be switched depending on some constraints which can be defined by the user but which we are not discussing here.
In case the application uses that backend model A in the backend, everything is okay and the application does what it is expected to do.
But: In case ZCL_BACKEND_MODEL_B does its work, an output stating e.g. that the backend data is derived by an external system should be shown as an info message in webdynpro.

This simple requirement led to two approaches.

Approach #01

The first approach is to apply specific methods which allow the UI to get some kind of a message from the backend.
IoC implemented - Approach 01
This means, method GET_INFO_MESSAGE ( ) needs to be called by the UI and depending on wether or not some message has been specified, the message has to be displayed.
This is a quite simple approach.

Disadvantage of approach #1

But there is also a disadvantage to this approach: By extended the interface with the GET_INFO_MESSAGE-method, we introduce a new concern to the application, which is completely useless for backend model A. Only backend model B will ever provide some messages to its caller (which is, the UI).
Even worse: Every implementer of ZIF_SOME_BACKEND_MODEL will have to implement at least an empty implementation of that method to avoid runtime exceptions when a not implemented method is called.

Approach #2

The second approach makes use of the Inversion of Control principle. Instead of asking the backend something, the UI tells it to tell its message manager what it thinks is newsworthy to tell.
How does it work? The most crucial difference is the existence of a new interface ZIF_I_AM_COMMUNICATIVE. It is implemented only by Backend Model B.
IoC implemented - Approach 02

What happens in the application? The UI tries to downcast the backend model to an instance of type ZIF_I_AM_COMMUNICATIVE. If backend model A is currently used in the application, the downcast will fail and the exception CX_SY_MOVE_CAST_ERROR may be catched silently.
In case the downcast succeeds, the webdynpro message manager of the UI component will be transferred to the backend model.
This gives backend model B the opportunity to show a warning / info message to the UI informing the user that backend model B is active. This could happen at a time point of time which can be controlled by the backend, not by the UI.

Disadvantage of approach #2

But also approach #2 has its disadvantages. By introducing a webdnypro interface to the backend, we have not only a dependency of the UI to the backend, but also bring in a dependency in reverse. At least as far it concerns ZCL_BACKEND_MODEL_B.
Usually you should avoid having such kind of dependencies as they might imply problems when working with dependency tracking techniques such as IoC Containers.
Also, the coupling between backend model B and the webdynpro runtime is increased by introducing the interface IF_WD_MESSAGE_MANAGER to the backend class.
To avoid these issues you might consider to wrap IF_WD_MESSAGE_MANAGER inside another, more abstract class, e.g. a generic message manager interface whose implementers may also work with classic dynpro screens. Instead of the webdynpro message manager, this wrapper class instance would then be injected to the backend. However such an approach might be kind of over-engineering in the first step.

What to do?

We decided to go for approach #2 as it was the least invasive and the most flexible one. We might consider implementing the refactoring mentioned in the disadvantages section of approach #2 in the future. However, the approach works like a charm right now.

Advertisements

The issue with having many small classes

Aftermath

The previous blog post was about applying design principles to the architecture of a simple parser application, which served as an exercise application. It led to quite a few, small classes in the end which all had their own responsibility.
Big Picture

Issue

As most of the classes deal with solving a certain aspect of the exercise, one class has the responsibility to arrange the work of the other classes to solve what needs to be solved. This class implements the interface ZIF_OCR_PARSING_ALGORITHM . An instance of that class is given to every consumer which needs to parse some sample input.
Every low level class that this class is dealing with to “orchestrate” the program flow, is registered as an dependency. Dependencies can be declared in the constructor of this course grained class. This makes creation of that class impossible without satisfying its dependencies.

methods CONSTRUCTOR
importing
!IO_FILE_ACCESS type ref to ZIF_OCR_FILE_ACCESS
!IO_LINES_SEPERATOR type ref to ZIF_OCR_LINES_SEPERATOR
!IO_CHARACTERS_SEPERATOR type ref to ZIF_OCR_CHARACTERS_SEPERATOR
!IO_CHARACTER_PARSER type ref to ZIF_OCR_CHARACTER_PARSER .

Providing dependencies during object creation by an external caller is called “dependency injection” (DI) as opposed to having classes that create their dependencies on their own. As the creation of the low level instances is not in the control of the class that orchestrates the parser flow, this paradigm is also called “Inversion of control” (IoC).
However, this approach has another challenge ready. That issue has been described very clearly by Oliver as a reply to the previous blog post
“Injection in the constructor – even to an interface – looks like quite tight coupling to me as it leaves the consumer of the coarse-grained class (the algorithm, in your case) to instantiate the composites.”

Factory

Of course the consumer of the composite object model should not create it on its own. One possibility would be to utilize the factory pattern. Instead of composing the main object and all of its dependent objects on its own, the consumer delegates this task to a factory method, which could be a static method call in its most simple realization:
DATA lo_parser TYPE REF TO ZIF_OCR_PARSING_ALGORITHM.
lo_parser = ZCL_PARSER_FACTORY=>CREATE_NEW_PARSER( ).

Within CREATE_NEW_PARSER( ) the same magic takes place as before: in the first step, all low level objects are created, in the second step the main parser object is created, while its dependencies declared by the CONSTRUCTOR are satisfied. This approach is called “Poor man’s dependency injection”

*alternative 1: poor man's DI
DATA lo_ocr TYPE REF TO zif_ocr_parsing_algorithm.
DATA lo_char_sep TYPE REF TO zif_ocr_characters_seperator.
DATA lo_char_parser TYPE REF TO zif_ocr_character_parser.
DATA lo_file_access TYPE REF TO zif_ocr_file_access.
DATA lo_lines_seperator TYPE REF TO zif_ocr_lines_seperator.
CREATE OBJECT lo_char_sep TYPE zcl_ocr_characters_seperator.
CREATE OBJECT lo_char_parser TYPE zcl_ocr_character_parser.
CREATE OBJECT lo_file_access TYPE zcl_ocr_gui_file_access.
CREATE OBJECT lo_lines_seperator TYPE zcl_ocr_lines_seperator.

CREATE OBJECT lo_ocr TYPE zcl_ocr_parsing_algorithm
EXPORTING
io_character_parser = lo_char_parser
io_characters_seperator = lo_char_sep
io_file_access = lo_file_access
io_lines_seperator = lo_lines_seperator.

IoC Container

However, the task of manually creating dependent objects in order to hand them over to a course grained object when it is created, can be automated. Think of the facts that are known:

  • all low level interfaces like ZIF_OCR_CHARACTER_PARSER have one class that implements each of them
  • instances of these classes could be created dynamically, as their constructors currently require no input parameter
  • the course grained interface ZIF_OCR_PARSING_ALGORITHM also has a specific class that implements it
  • however, its constructor requires some instances of the low level interfaces
  • as these required parameters are instances of the low level interfaces, these dependencies could be satisfied automatically

As a brief summary, this is exactly what an IoC Container should do, also refered to as DI containers.
The registration of the interfaces and classes is done programmatically in the following example. However, it may be moved to customizing in order to “free” the code of having hard references to explizit class names

*alternative 2: IoC Container
DATA lo_ioc_container TYPE REF TO /leos/if_ioc_container.
lo_ioc_container = /leos/cl_ioc_container=>create_empty_instance( ).
lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_characters_seperator' iv_implementer = 'zcl_ocr_characters_seperator' ).
lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_character_parser' iv_implementer = 'zcl_ocr_character_parser' ).
lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_file_access' iv_implementer = 'zcl_ocr_gui_file_access' ).
lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_lines_seperator' iv_implementer = 'zcl_ocr_lines_seperator' ).
lo_ioc_container->add_implementer( iv_contract = 'zif_ocr_parsing_algorithm' iv_implementer = 'zcl_ocr_parsing_algorithm' ).

No matter, how the classes have been registered, either in the code or by customizing, the caller now only needs to call the container and request an instance from it at startup:

DATA lo_ocr TYPE REF TO zif_ocr_parsing_algorithm.
*create the container either by reference to customizing or by the registration coding shown above...
*...
lo_ocr ?= lo_ioc_container->get_implementer( iv_contract = 'zif_ocr_parsing_algorithm' ).

Stories about Repositories and Number Ranges

Introduction

This week, I faced a situation where I needed to implement a new business object for one of our applications. As business objects usually need persistence, I created a new repository for it to handle the database access. This repository covered the standard database functions like Create, Update, Read and Delete (CRUD).
While implementing the repository, the question of what should happen to new business objects during the Create-operation came up, specifically who should create a new document number. I decided to request new number ranges for new business objects while in the SAVE-Method of the repository and then the trouble started to grow…

Bad design

The first design was a mess:
v1
In this fictitious example, ZCL_BOOKING represents an entity, a booking record, which will be saved to the database.
ZCL_BOOKING_REPOSITORY takes care of saving this entity correctly, with some more functions like retrieving those entities back from database or delete them from the DB.
The SAVE-Method looked like this:
CLASS ZCL_BOOKINGS_REPOSITORY IMPLEMENTATION.
...
METHOD ZIF_BOOKING_REPOSITORY~save.
DATA lv_bookid TYPE S_BOOK_ID.
DATA ls_header TYPE bookings.
lv_bookid = io_booking->get_bookid( ).
ls_header = io_booking->get_booking( ).
IF lv_bookid IS INITIAL.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = 'ZFL_BOOKID'
object = '01'
IMPORTING
number = rv_bookid
EXCEPTIONS
interval_not_found = 1
number_range_not_intern = 2
object_not_found = 3
quantity_is_0 = 4
quantity_is_not_1 = 5
interval_overflow = 6
buffer_overflow = 7
OTHERS = 8.
* implement proper error handling here...
io_booking->set_bookid( lv_bookid ).
CALL FUNCTION 'Z_FM_INSERT_BOOKINGS' IN UPDATE TASK
EXPORTING
iv_bookid = lv_bookid
is_header = ls_header.
ELSE.
CALL FUNCTION 'Z_FM_UPDATE_BOOKINGS' IN UPDATE TASK
EXPORTING
iv_bookid = lv_bookid
is_header = ls_header.
ENDIF.
ENDMETHOD.
...
ENDCLASS.

Running out of time? I’M RUNNING OUT OF NUMBERS!!!

Everything went well, until I started to write a unit test for the SAVE-method.
I started to write a test which created a new instance of type ZCL_BOOKING without any BOOKID. I expected the instance to be inserted to the database. This worked pretty well, but when I tried to implement the TEARDOWN method I had some issues. As Unit tests need to be processed as often as you need, and should leave the system in the same state from where you started from. This means, the inserted record needed to be deleted.
As the SAVE-method requests a new number for each object that has not yet an ID, I don’t really know which ID has just been inserted.
I could have asked the instance of type ZCL_BOOKING which ID has been set for it. This would solve at least the issue that I need to clean up the database after the test-insert.
But, more severe, the current number in the number range interval has increased by one with each unit test. This was not acceptable.
So the unit test revealed the bad design: In fact, the repository had a dependency on a number range object. Actually, it should not care about it.

Refactored design

This step introduces a new class to the design, which is called ZCL_NUMBER_RANGE_REQUEST. It implements an interface ZIF_NUMBER_RANGE_REQUEST which is now used by ZCL_BOOKING_REPOSITORY to handle its number range requests.
The number range object is created before ZCL_BOOKING_REPOSITORY is getting created in order to hand it over to the constructor of the repository.

v2

The result is: Instead of creating new document numbers by its own, the repository asks another object for it.
This has a huge benefit: As the number range object is specified by an interface, we can fake this interface in a unit test and pass it to the repository’s constructor. The fake object of course does not request a real number from a real number range but returns “1” all the time.

Unit Test Setup

So that’s what the new implementation of the classes looks like:
CLASS ZCL_BOOKING_REPOSITORY IMPLEMENTATION.
...
METHOD ZIF_BOOKING_REPOSITORY~save.
DATA lv_bookid TYPE S_BOOK_ID.
DATA ls_header TYPE bookings.
lv_bookid = io_booking->get_bookid( ).
ls_header = io_booking->get_booking( ).
IF lv_bookid IS INITIAL.
lv_bookid = mo_number_range->get_next_number( ).
io_booking->set_bookid( lv_bookid ).
CALL FUNCTION 'Z_FM_INSERT_BOOKINGS' IN UPDATE TASK
EXPORTING
iv_bookid = lv_bookid
is_header = ls_header.
ELSE.
CALL FUNCTION 'Z_FM_UPDATE_BOOKINGS' IN UPDATE TASK
EXPORTING
iv_bookid = lv_bookid
is_header = ls_header.
ENDIF.
ENDMETHOD.
METHOD constructor.
mo_number_range = io_number_range.
ENDMETHOD.
ENDCLASS.

The implementation of the number range object requests the current number by using the standard function module. The input paramaters for this function module have been provided in the CONSTRUCTOR of the object.

CLASS ZCL_NUMBER_RANGE_REQUEST IMPLEMENTATION.
...
method GET_NEXT_NUMBER.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = mv_nrobj
object = mv_nrnr
IMPORTING
number = rv_number
EXCEPTIONS
interval_not_found = 1
number_range_not_intern = 2
object_not_found = 3
quantity_is_0 = 4
quantity_is_not_1 = 5
interval_overflow = 6
buffer_overflow = 7
OTHERS = 8.
* implement proper error handling here...
endmethod.
ENDCLASS.

How can I really get a fake?

Fake objects can be created using local classes in the unit test. As an alternative, mocking Frameworks can help to automate this task by providing a declarative API.
In the real life use case I created the fake object using a mocking framework with this call:
mo_number_range_request ?= /leos/cl_mocker=>/leos/if_mocker~mock( ‘/leosb/if_number_range_request’ ) ->method( 'GET_NEXT_NUMBER' )->returns( 1 )->generate_mockup( ).

I hate puzzles

This kind of architecture eventually leads to tons of classes, each having their own responsibility. In a real life application you would need to set up the repository instances and their dependencies at least in some specific method at startup:
CREATE OBJECT lo_number_range TYPE ZCL_NUMBER_RANGE_REQUEST
CREATE OBJECT lo_repository TYPE ZCL_BOOKING_REPOSITORY EXPORTING io_number_range = lo_number_range.

IoC Containers help you in managing these dependencies by allowing you to register specific classes for each interface in customizing. Their purpose is to resolve all dependencies for a root object, when a root object like a repository is requested by the application. The container creates this root object and hands it back to the caller with just one single line of code.

Related links

IoC Container
Mocking Framework

IoC Container in ABAP Part II

Introduction

In the first part of this series I introduced a file backend which supports multiple types of storage types and allows the combination with authorization check or file filter classes.

The design pattern in use is the decorator pattern. Every class which implements additional functionality declares the usage of a member instance which is described by the same interface which is already implemented by the class itself.

This allows us to have more flexibility than in another approach which used inheritance.

Having a decorator based file access backend and a factory which creates and combines these instances for you comes close to what the requirements said. But already the very first requirement – “Multiple storages will need to be supported by configuration, e.g. cluster database storage or SAP Generic Objects Services based storage” is not yet satisfied: There is no possibility to configure the object hierarchy yet.

In this blog post I’m going to show you how this can be achieved by utilizing an IoC Container.

Configuring the IoC Container by API

The decorator pattern allows us to have the flexibility we need. The instances may be combined without any restriction since they implement the same interface. But this approach is still a static one, because of the way the objects are created.

If this coding was part of a factory, you would need to have a new method every time you request a slightly new combination of object instances.

In software which is to be shipped to customers, having a factory which is not extensible would be too restrictive.

The coding which creates and combines all the instances could also be implemented by a BADI. The advantage would be that every customer could implement its own file backend by combining certain instances.

Unfortunately, in the first part of this series, one requirement stated clearly that there could be multiple scenarios in which the file access could be used: “within the same system, there could be multiple scenarios, which require different kinds of storage types”

So what would be the solution? Have a BADI implementation for each of these scenarios?

With an IoC Container you could define freely combined object hierarchies. This definition could be done in source code but could also be done in customizing.

The basis of the configuration of an IoC Container is the contract. The contract is, technically spoken, an ABAP OO Interface.

There could be one or multiple implementers for each contract. Implementers are, technically spoken, ABAP OO classes which implement this interface.

The easiest example which shows the usage of an IoC Container is the creation of one class for one interface:

The IoC Container is created using the following call:

DATA lo_ioc_container TYPE REF TO /leos/if_ioc_container.

*       create IoC container

lo_ioc_container = /leos/cl_ioc_container=>create_empty_instance( ).

Next, a contract and its implementer need to be registered:


*       register instance for binary access

lo_ioc_container->add_implementer(

EXPORTING

iv_contract = '/leos/if_ioc_demo_bin_access'

iv_implementer =  '/leos/cl_ioc_demo_bin_access' ).

The instance can be resolved with the GET_IMPLEMENTER-method of the IoC Container.

This example does not have any benefit yet. The implementation is not yet defined in the customizing and we didn’t even combine two instances with each other. The combination comes next.

If there are multiple implementers for one contract, they can be distinguished by using a filter value.

This filter value can be handed over when the GET_IMPLEMENTER-Method is called.

Since we need to combine two instances directly with each other, we use another basic concept of the IoC Container: Configuration of Constructor Injection. In the example the internal table LT_CI takes care of the constructor injection

Requesting the implementer still works the same way we’ve already seen before.

The whole code looks like this:

The debugger shows how this object is nicely nested:

The IoC Container will resolve all dependencies, which are declared by constructor injection, automatically for you.

If the implementer of the dependency declares its own dependencies, they are of course also resolved before the object is getting created – which means that you can nest dependencies as deep as you want.

Features like lifetime control, Setter-Injection, passing parameters by the application or passing default values to a constructor are also included.

Customizing

Up to this point, there was still a drawback: We defined the implementers directly in the coding at design time. Manually nesting these implementations would still be easier. So let’s shift all the setup routines of the previously shown coding to the customizing.

The whole definition starts with an application. It’s up to you which name you chose.

Now let’s declare the contracts. There is only one in this application. Since we want the IoC Container to create a new instance every time we ask for it, we set the lifetime to “transient”

There are two implementers yet, one is the default implementer, while the other one is only valid if a filter value ‘BA’ has been passed by the application or has been declared in the constructor injection configuration.

The constructor injection configuration of /LEOS/CL_IOC_DEMO_ACCESS_AUTH comes next:

That’s it – we are done.

The whole coding to get an instance can be simplified to this piece of code:

Conclusion

The setup of complex object hierarchies can be dramatically simplified with the help of an IoC Container. The container takes care of the creation of the objects you need, satisfies all required dependencies and controls the lifetimes. Changes to the hierarchy are possible by only changing the customizing. The code is never again touched for this type of change.

IoC Container in ABAP Part I

Introduction

This is the first part of my new series “IoC Container in ABAP”.

“IoC” is an abbreviation of “Inversion of Control” which is a programming paradigm. Instead of describing it in endless boring chapters I’m going to give you an example today.

Today I’m going to give you an introduction in the scenario which we are going to deal with through the upcoming blog posts.

Bad news is: There will be no IoC Container shown today.

Good News is: I’m going to prepare an application architecture which will match perfectly to what an IoC Container should usually automate. There will be bad architecture and there will be a not so bad one.

Bad news II is, that this example is not really an example which shows the full power of an IoC Container. In fact, for an IoC Container it is a very tiny example which could also be realized without such a container. However this small example illustrates how dependencies work and should be used. And that is what an IoC is about in the first step (beside many other aspects).

Imagine, a new file access component would have to be designed in pure ABAP, having the following requirements

  • Multiple storages will need to be supported by configuration, e.g. cluster database storage or SAP Generic Objects Services based storage
  • The client of the component should not be aware of the implementation details, hence, should not need to know which implementation is chosen
  • Having an extensible backend, which supports the following features:
    • authorization checks when saving the files
    • authorization checks when accessing the files
    • file filters
    • multiple storage types (as mentioned above)
    • the above mentioned features need to be configurable based on the usage scenario
  • within the same system, there could be multiple scenarios, which require different kinds of storage types

This blog post is not going to deal with the implementation details of these features – it is going to deal with the architecture which allows us to support the kind of flexibility we require.

Interfaces

So what to do? Go and create a new file backend class which performs hard coded authorization checks on its own authorization object? Or which filters files based on some customizing? Check customizing and switch to sub-methods using IF- or CASE -Statements?

No – let’s start with some interface descriptions in order to get an idea of what a file backend should provide. Let’s do not take care of any implementation details.

In this demo it is going to be a pretty simple interface description. The only operations which will be supported are saving and retrieving file contents. There will be no file lists. And there will be no directories, not in this demo.

In fact, this is the only interface which will be shown today.

Drawbacks of having one class

If the required functionality was only implemented by one class, there would be certain drawbacks:

  • Authorization checks would have to be implemented in the same class which also deals with storing files to the backend. Having more than one responsibility for one class is a clear violation of the Single Responsibility Principle
  • Same for the file filter – it would be again a violation of the Single Responsibility Principle
  • Additional functionalities would not be testable in an isolated environment
  • Customer specific changes, e.g. in the way files are stored in the backend would require to change the existing class. This is a violation of the Open Closed Principle.
  • If the file access service would be used in various scenarios, a certain kind of file access would be chosen every time. This violates the principle of having loosely coupled components

Realization Variant 1 – Inheritance

The file access is described by the interface /LEOS/IF_IOC_DEMO_BIN_ACCESS.

The classic approach is to have classes which inherit from each other. The authorization check would inherit from the file access class. Every time a method is called, the authorization check class performs its checks before the super class method is called. Same for the file filter. Any client uses in fact not the file access, but rather one of its sub classes.

Unfortunately this approach has some severe drawbacks. If the file access needs to be replaced by another implementation which access a different kind of file repository, the whole inheritance hierarchy would have to be created again, causing the creation of two classes for each file filter and authorization check, but having the same responsibility.

The written classes are not reusable since they are caught in an inheritance hierarchy. Further requirements would result in further child classes.

And any super class functionality that exists in between cannot just be switched off if the use case or the customer requirements ask for it.

So in terms of maintenance and extensibility, features that have been explicitly written down at the top of this blog, this approach is a bad one.

Realization Variant 2 – Decorator

So instead of having big inheritance hierarchies, we use the decorator pattern instead. Every new class implements  the interface /LEOS/IF_IOC_DEMO_BIN_ACCESS on its own.

Every class which implements additional functionality, like authorization checks or file filters, has a member instance MO_BIN_ATT_ACCESS type with the same Interface /LEOS/IF_IOC_DEMO_BIN_ACCESS.

Validations or checks can be done in the specific method of the specific class which is responsible for the implementation of the feature, and if certain checks fail, they are not going to be delegated to the member instance.

The classes declare the dependencies by their constructors. The classes /LEOS/CL_IOC_DEMO_ACCESS_AUTH and /LEOS/CL_IOC_DEMO_FILTER do so by expecting another instance of type /LEOS/IF_IOC_DEMO_BIN_ACCESS in their constructor.

This means, any additional functionality and any file access class are freely combinable, since all of them implement the same interface and request a nested instance in their constructor.

This means, any additional functionality and any file access class are freely combinable, since all of them implement the same interface and requested a nested instance in their constructor.

A client may create the file backend by calling a factory which creates and combines the instances.

This piece of code shows the creation of a file access instance and combines it with an authorization check instance. In any upcoming example the file filter is not shown since its existence makes no difference from the architectural point of view. But the examples are getting smaller, hence better to understand.

Having a decorator based file access backend and a factory which creates and combines these instances for you comes close to what the requirements said. But already the very first requirement – “Multiple storages will need to be supported by configuration, e.g. cluster database storage or SAP Generic Objects Services based storage” is not yet satisfied: There is no possibility to configure the object hierarchy yet.

In my next blog post I’m going to show you how this can be achieved by utilizing an IoC Container.