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: 
keremkoseoglu
Contributor

This article started with my blog post Suggestions for better object oriented design . After I have published it on LinkedIn, @ennowulff  started a friendly discussion about composition vs inheritance; and sent me a GitHub repo containing his design based on composition. I have suggested some improvement ideas based on dependency injection; and with his kind permission, I’m publishing them here.

Initial design

Here is the initial UML provided:

uml 01.png

First of all, let’s get rid of the MAIN class. Because the purpose is to get rid of inheritance. Afterwards; we will see how to centralize different behavior. For the sake of readability, I will leave the previously overridden method names in sub-classes.

uml 02.png

Separate display

First of all; following the MVC logic, it is suggested to separate business logic from display logic. Therefore, displaying a document should be handled by a different mechanism. Here is how I would set it up.

uml03.png

On the left side, I have the ZIF01_AREA_DISPLAY interface with a single method: DISPLAY_AREA. This method would import an internal table, work area, etc containing the parameters required for display. Different display classes X, Y, Z, etc. implement the details of different display options. X could be WRITE, Y could be ALV GRID, Z could be HIERARCH.ALV, etc… 

During runtime, the main program can determine what kind of a display is needed; and create the corresponding ZCO01_AREA_DISP_* class + cast it as ZIF01_AREA_DISPLAY. For the sake of simplicity, I will go hard-coded. Example:

 

 

DATA(my_area) = CAST zcoi01_area( NEW zco01_area_c( ) ).
" Do stuff with my_area, then finally:
DATA(my_display) = CAST zif01_area_display( NEW zco01_area_disp_y( ) ).
my_area->display( my_display ).

 

 

In ZCO01_AREA_A – DISPLAY:

 

 

" Prepare AREA A ITAB in display format, then:
display->display_area( display_formatted_itab ).

 

 

In ZCO01_AREA_B – DISPLAY:

 

 

" Prepare AREA B ITAB in display format, then:
display->display_area( display_formatted_itab ).

 

 

Now; I can hear the question already: What is AREA_A and AREA_B have the exact same ITAB structure and gonna do the exact same thing in display? Isn’t it better to centralize the code over composition?

I think not! We are going step by step.

Separate documents

Actually; “document” concept should be a whole distinct structure! Check this out.

uml 04.png

In the top part, I have a new interface called DOC_COLLECTION, and it has different implementations as X and Y. They are free to behave differently.

Now, my main program is free to pick any DOC_COLLECTION implementation during runtime. It can depend on the selection screen, user settings, a Z-table, etc… For the sake of simplicity, I will go hard-coded again.

 

 

DATA(my_doc_coll) = CAST zif01_doc_collection( NEW zco01_doc_coll_y( ) ).

DATA(my_area) = CAST zcoi01_area( NEW zco01_area_c( ) ).
my_area->set_doc_collection( my_doc_coll ).

" Here, my_area can do anything it wants with documents.
" It doesn’t care what kind of a document class lies beyond the interface.
" If you need a new behavior, you can simply add a new document collection class and customize.
" Document / area class dependency is close to zero.

DATA(my_display) = CAST zif01_area_display( NEW zco01_area_disp_y( ) ).
my_doc_coll->display( my_display ).

 

 

Now; despite the fact that Z*AREA* classes don’t have any further common functionality, there is one more thing that bothers me. Ideally, you should not ask Z*AREA* objects for data. Data is intimate. Instead; you should tell Z*AREA* objects (which have the data) to do tasks for you.

Separate e-mail

Let’s make it more clear with an example. Why are you asking you those objects for a name, responsible person and countries? I will assume that you will gather this information and send an e-mail containing basic data + some document info. It can be anything else too, obviously. In my case, the design would look like this.

uml 05.png

At the bottom, I have a new E-Mail interface. It takes some parameters and sends an E-Mail, obviously. Z*AREA*EMAIL*X and Z*AREA*EMAIL*Y could be different E-Mail formats. 

Now, I don’t need to ask Z*AREA* classes for their private, intimate data structures. That would be “data oriented programming”. Instead, I simply pass them an E-Mail interface, and ask them to work for me. Their task is to prepare data and pass to ZIF01_EMAIL – SEND, that’s all. They don’t have to prepare E-Mail formats or put people into CC: or something, just prepare and pass data. Rest will be handled by the E-Mail implementation class.

This is how the main program might look at this point; again with hard coded class names for simplicity:

 

 

DATA(my_area) = CAST zcoi01_area( NEW zco01_area_c( ) ).

DATA(my_doc_coll) = CAST zif01_doc_collection( NEW zco01_doc_coll_y( ) ).
my_area->set_doc_collection( my_doc_coll ).

DATA(my_email_format) = CAST zif01_email( NEW zco01_area_email_x( ) ).
my_area->send_email( my_email_format ).

DATA(my_display) = CAST zif01_area_display( NEW zco01_area_disp_y( ) ).
my_doc_coll->display( my_display ).

 

 

Evaluation

As you see; the concepts of document collection, document display, e-mail and area are completely isolated at this point. No class touches another class directly. Interfaces touch, and that’s the purpose in the first place!

In different programs / circumstances; you can combine different doc.collection, doc.display, e-mail, area, etc… classes however you want, and architecturally, it would work just fine every time. And when you modify the internal structure of a class, you don’t have to worry about affecting another class.

In this architecture, you can freely create new implementations, and the rest of the system would welcome the new classes perfectly well; because they must abide by the standards set by their interfaces. 

Now, maybe you don’t need a different implementation for each area either? It could be possible to manage everything over a single area implementation via Z-Tables. Or, you can have an area implementation for each area type, and based on their behavior, 100 areas can be summarized over 3 area types. 

The final architecture above may not be perfect, and it’s certainly not the only solution; but it “scales” easily, flexibly and definitely not logarithmically. As the number of classes increases, the advantage would be even bigger.

I hope that this demonstrates the basic idea to prefer composition over inheritance. We didn’t even have to use the servant design pattern. Instead, dependency injection mostly saved the day.