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: 
Former Member

Hello everyone,

as this is my very first blog post, I really would like to give a very short introduction to myself. I live with my family in Ingelheim, Germany. A small town located near Mainz in the beautiful wine region of Rhinehessen (Rhineland-Palatinate).

I work since 20 years mainly as a functional consultant in the area of accounting and controlling. But I have a dark side...I love programming :twisted:

The recent years I have changed my programming style from the procedural style to more object oriented style. Actually it was a quite hard transition and took me a while to fully understand the concepts behind it.

Over the years I realized that my developments still are not well designed, even if they are object oriented. Every time when new requirements came in, I had to change a lot of the existing code. Also I wanted to be sure that the new code did not break the existing code. And in addition I was looking for some kind of methods, where you can do developments on your own system and not the final customers environment, but still be able to test the code with proper test data. After researching and studying for a while I found what is known as the SOLID Design Principles and Test-driven development. Since this time I try to follow those principles whenever it is possible.

There is one concept which I discovered recently and I thought this is something worth to give it a try. The original design is described here https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91. I have transformed the examples from there into ABAP and thought others might be interested into the design as well. So here it is.

The basic idea behind the whole concept is that methods should either perform an action (Commands) or return data (Queries), but not both. This concept is also known as Command-Query Separation (CQS). We will have a look at the command site for now.

We first need an Interface which represents our command.


interface lif_command.
endinterface.

Well, this doesn't look to complicated. The next thing we need is our interface for the command handler.


interface lif_cmd_handler.
  methods handle importing i_command type ref to lif_command.
endinterface.

We are now able to decouple the business logic from the data. The command handler now operates on the command that we provide. Using interfaces give us a lot of flexibility, which we will see later.

We can now create our concrete command. As you can see our command is a pure data object without any logic (Setter and Getter methods would be possible). We could also create a data reference for our data, but I decided to use objects for data representation as well.


class lcl_move_customer_cmd definition.
  public section.
    interfaces lif_command.
    data customer_id type i.
    data new_adress  type string.
endclass.
class lcl_move_customer_cmd implementation.
endclass.

Here is the implementation of our concrete command handler.


class lcl_move_customer_cmd_handler definition.
  public section.
    interfaces lif_cmd_handler.
endclass.
class lcl_move_customer_cmd_handler implementation.
  method lif_cmd_handler~handle.
    data cmd type ref to lcl_move_customer_cmd.
    cmd ?= i_command.
    cl_demo_output=>write( 'I handled the command.' ).
    cl_demo_output=>write( 'Customer:' && ` ` && cmd->customer_id ).
    cl_demo_output=>write( 'Adress:' && ` ` && cmd->new_adress ).
  endmethod.
endclass.

The command handler receives our command and process the data. The only thing here which is not nice, that we have to cast to the concrete command type ( cmd ?= i_command ). The original C# code shows some kind of type checking using generics which is something that is not available in ABAP. At least I haven't found anything in ABAP. So, if somebody knows a possibility to avoid the casting here, please let me know.

We now need the controller which knows how to operate.


class lcl_controller definition.
  public section.
    methods constructor   importing i_handler type ref to lif_cmd_handler.
    methods move_customer importing i_customer_id type i i_new_adress type string.
  private section.
    data handler type ref to lif_cmd_handler.
endclass.
class lcl_controller implementation.
  method constructor.
*   Constructor injection. Assert ensures that controller is working correct
    assert i_handler is bound.
    me->handler = i_handler.
  endmethod.
  method move_customer.
    data(cmd) = new lcl_move_customer_cmd( ).
    cmd->customer_id = i_customer_id.
    cmd->new_adress  = i_new_adress.
*   Passing the data object to the handler
    me->handler->handle( cmd ).
    cl_demo_output=>display( ).
  endmethod.
endclass.

The handler will be injected into the controller via constructor injection


class lcl_main definition.
  public section.
    class-methods start.
endclass.
class lcl_main implementation.
  method start.
    data(handler) = new lcl_move_customer_cmd_handler( ).
    data(controller) = new lcl_controller( handler ).
    controller->move_customer( i_customer_id = '12345' i_new_adress = 'The new adress' ).
  endmethod.
endclass.

Now we come to the most interesting part. As our command handler is based on a abstraction, the command handler interface, we are now able to create simply a Decorator for the handler, which means we can add additional functionality like validation or implementing cross cutting concerns like logging without changing the existing code.


It is just a simple example, but I think you can imagine how powerful this can be.


class lcl_customer_cmd_decorator definition.
  public section.
    interfaces lif_cmd_handler.
    methods constructor importing i_decorated_handler type ref to lif_cmd_handler.
  private section.
    data decorated_handler type ref to lif_cmd_handler.
endclass.
class lcl_customer_cmd_decorator implementation.
  method constructor.
*   Constructor injection.
    assert i_decorated_handler is bound.
    me->decorated_handler = i_decorated_handler.
  endmethod.
  method lif_cmd_handler~handle.
    data cmd type ref to lcl_move_customer_cmd.
    cmd ?= i_command.
    cl_demo_output=>write( 'I decorated the command before.' ).
    cl_demo_output=>write( 'Customer ID validated.' ).
    me->decorated_handler->handle( cmd ).
    cl_demo_output=>write( 'I decorated the command after.' ).
    cl_demo_output=>write( 'Save Customer ID with new adress.' ).
  endmethod.
endclass.

All we have to do is setting up the handler correctly with the decorated handler. For details about the Decorator Design Pattern you can look here.


class lcl_main implementation.
  method start.
    data(handler) = new lcl_customer_cmd_decorator( new lcl_move_customer_cmd_handler( ) ).
    data(controller) = new lcl_controller( handler ).
    controller->move_customer( i_customer_id = '12345' i_new_adress = 'The new adress' ).
  endmethod.
endclass.

The output will be now:

A while after I discovered the original blog post, I noticed that there is already a formal description for the concept. The design is described as the Command-Processor Pattern.

I hope you'll find this blog useful and gave you some inspiration. Also if you find something that can be improved or enhanced, please let me know. The next time I will show an example for the query part.

The complete source code for the Example report can be found here.

Best regards,

Tapio

3 Comments