Abstract Driven Development
The last few years I spent a lot of time reading and experiencing some programming paradigms. I talked to people, read books, blogs and made my own experiences. In 2013 I wrote the german ABAP Kochbuch with the help of four of my colleagues. In this book we tried to show some techniques that could simplify your work and enhance the quality of your programs.
One of the main techniques we try to teach is modularization. This is one of the oldest programming paradigms and one of the main principles in software design. Separating your functions in smaller units will make testing easier and the program will be easier to read.
Last week I had the idea that this could be the era of a new design principle:
The era of Abstract Driven Development.
What Is It About?
The aim of ADD (I really invented a new term! I can’t believe it! 😉 ) is to getting things on the most possible abstract level. It’s not only to modularize to max (or better: to the minimum…) but to also to put the code into general, globally accessible functions. The way I do this is the following:
- Check out which functionality can be moved to a distinct function. Do this. Think about the needed parameters. Create the new function and call the new method.
- Check, which other functions belong together.
- Think about other functionalities that could be helpful or useful in this context.
- Create a new class and move the methods to this class.
- Name the class and the methods in a general way. Avoid specific terms.
- Test the new class in different contexts.
- Talk to your colleagues about the functionality. Ask them for ideas and how to improve or simplify the code.
- Document the class and its functions.
- Build an application out of it
- Use parameters
- Use customizing tables
- Add helper functions
- Add dialogs
- Provide demonstration reports
- Spread the knowledge about the created functionality.
Trial And Error
As every new technique or principle you would like to learn you will have to try, try and try again. Practice. Programming abstract and general modules will need a lot of practice. You can only estimate the pros and cons of a new technique if you do try them. If you try you will do errors and you will find things complicated and long-winded. This is the normal process. At least it will help you having the experience with this kind of thinking.
Even if you do not use test units or design patterns, it will help you to have the knowledge about it; to know about the advantages and disadvantages. This is the main reason for my concept of Abstract Driven Development: Widen your horizon and make you think about different options. I would never propagate this kind of programming as the one and only or to tell you that you will have to program this way to get the perfect program.
In my opinion it is very useful and indispensable to learn new things and try different ways of thinking.
Tips For Programming
I admit that it is not easy to be general and unspecific. It is easier to program a specific and detailed function. I heard that the special theory of relativity is much easier than the general theory of relativity. Therefore I postulate that this rule is also true for programming. 🙂
But now let me tell you some tips for getting abstract:
- Learn the advantages of the features of object oriented programming
- Up- and Down-casts
- Returning values: CHECK zcl_tool_class=>can_you_do_this( ) = ABAP_TRUE.
- learn about using design patterns
- Factory methods
- Observer pattern
- The decorator pattern
- and some others
- Be prepared to have more work in the first instance
- more thinking
- making errors
- falling into traps
- running into one-way-streets
- struggling which will be the best way to do something
- a lot of refactoring
- Be sure there will be benefits, like
- easier use in the future
- a lot of experience!
- genius functions at the end
- easily enhance functionality
Examples Of General Abstract Code
In this section I will try to specify what I mean with one detailed example I already mentioned in the Tricktresor: The Magic Filter.
The genesis of this idea came in a project where I had to analyse and check a lot of calculations within different views. To give you an idea of these data I will refer to the example I also used in my blog: material data.
Imagine that you have an application using similar data with different views:
- Material master
- stock information
- aggregated on plant level
- aggregated on stock level
- different calculations on
In this application you will have to check whether the totals, aggregations and calculations on material level, stock level etc. is correct. You will have to filter for material, storage location, plant, material type and so on. This can be very annoying if you will have to do so every day, every week for several times.
First Step: Activate The Filter
The idea was to define a global filter for all views at once. The first idea was to use the observer pattern for this task. Every ALV grid will have to register at the global filter class. The global filter class has a method where you can set a specific filter. You can see the first shot in the screenshot above: The filter was defined by simple selection option in the selection screen.
The filter will be set for all registered objects. The reference of these objects is being held in an internal table. Theproblem was that the method SET_FILTER of a grid will dump if the filter used a field that does not exist in the table of a registered grid. Therefore I had to check the field catalog of each registered grid and delete the field that do not exist in the grid before applying the grid.
Second Step: Spread My Filter
The next idea was to set up a filter in any of the registered grids and spread this filter to all of the registered grids. Therefor I had to add user specific functions to the grid toolbar. Thanks to the SALV-model this functionality can be easily realized and put into a separate class by using the CL_SALV_FUNCTION class.
When calling the function zcl_magic_filter=>spread_my_filter the current grid will be passed to the function. The method spread_my_filter reads the current filter settings and apply them to all other registered grids.
Third Step: Use A “Real” Filter Dialog
When using different tables and views there will be only a little fields that may be meaningful to use. Therefore I had the idea of using a structure that holds all reasonable fields for the application. Create a grid based on this structure and let the user filter within this dummy grid.
Using a dummy grid is not very handy. It is better to use the filter dialog that is also used within the grids. With function module LVC_FILTER you can directly call the filter dialog using a field catalog. Report REUSE_FILTER_1 demonstrates the use of the filter dialog.
The problem here is the F4 help value dialog. When using the filter, you will see only values that exist in the table. It is hardly possible to use a general F4 value help based on the domain fix values or check table. There is a callback routine for F4 value help but it’s not very easy to handle. Or I did not find the the correct modules for using this exit.
Step Four: Making The Filter Work for ALV-Grid And SALV-Model
Thanks to down cast abilities of parameters you can use TYPE OBJECT for passing the grid instance to the helper class zcl_magic_filter. Within the methods you can try to cast OBJECT to CL_GUI_ALV_GRID or to CL_SALV_TABLE.
The class CL_SALV_CONTROLLER_METADATA helps applying the filter to any grid.
Here is an example of how to get the fieldcatalog for any type of grid:
Importing parameter: the grid object ir_grid TYPE REF TO object.
Returning parameter: rt_fcat type LVC_T_FCAT.
data lr_salv type ref to cl_salv_table.
data lr_grid type ref to cl_gui_alv_grid.
lr_salv ?= ir_grid.
rt_fcat = cl_salv_controller_metadata=>get_lvc_fieldcatalog(
r_columns = lr_salv->get_columns( )
r_aggregations = lr_salv->get_aggregations( ) ).
lr_grid ?= ir_grid.
lr_grid->get_frontend_fieldcatalog( importing et_fieldcatalog = rt_fcat ).
Step Six: Applying The Filter On Current Selection
For easily applying the filter I could imagine to read the data of a selected line, check the globally defined fields for filtering, setting the filter with the selected values and spread this filter to all registered objects (see Step 2).
Knowing that this concept works I also think of the following functionalities:
- Set the same layouts allover the registered grids
- Marking any lines in all registered grids that match defined parameters
- Deleting all filters
- Hide or show fields
- all fields except of…
- only fields …
I hope I could inspire you to thinking in some a more global, a more general, abstract way. The most important thing to have a benefit with this kind of programming is to share the knowledge about the generated objects and their functionality. Using abstract functions may also be more difficult than using specific methods. In each case – when programming and when using general code – you will have to think a bit more out-of-the-box.
Abstraction is important to any design. I like the term "ADD". Also, the Tips sections covers almost all aspects of starting to think on abstract level.
I think, you would always have to spend more time on a design and don't want to repeat it as all design would be different 🙂 . You would end up having to use different combination of design patterns and principal to come up with a new design altogether.
BTW - what is step number 5? Is it go and get coffee while the ALV object is available from SALV model to work with? just kidding 😈
Step 5 was too abstract to present, I think 😉
I will see how to fill step 5...