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