Decoration Day – Questioning The Decorator Pattern
There are design pattern. And there are design pattern. There are some I use really often because they are clear to me in use and technique, like the singleton or the factory pattern, the observer and some others. The decorator pattern for some reasons is very hard for me to understand. I learned about it in a workshop years ago. But over the years there was no need for me to use it and therefor no need to understand it. I try to change this for some weeks now…
I am not completely through with it…
The decorator pattern itself is described often and the UML diagram can mostly be found there. Up to now I have no idea what to use the pattern for in an ABAP environment. Nevertheless I tried to build up an example to understand how it works and what I maybe could do with it someday.
Characteristics Of A Decorator
The main principle of a decorator pattern is, to decorate an existing object in a way that the object does not know about the decorations. But that is exactly my problem with this pattern: In which cases would I like to have something added to a class without knowing that there was something added? In most cases I had the decorations need to know at least something about the decorated object. That is mentioned as a disadvantage of this pattern: The decorations somehow need to inform the decorated object about what they have done. And this seems to me some kind of weird: using something that is especially decoupled and than building something to connect again.
How does the decorator work?
The decorator pattern creates a hierachical structure like the one you will get when using inheritance. The predecessor object will be passed to the decorator object when being instantiated and stores it in a class attribute. This happens with each new decorator that will be applied.
After all decorations are done, for example the price can be queried by asking the last decoration about it. This decoration then asks the predecessor decorator about its price, add its own price and passes back the result.
When you think about ice cream the way for getting the price might be the following:
crumble->get_price caramel_sauce->get_price cream->get_price vanilla->get_price chocolate->get_price cone->get_price cone: 0,00 chocolate: ( 0,00 ) + 1,00 vanilla: ( 1,00 ) + 1,00 cream: ( 2,00 ) + 0,50 caramel_sauce: ( 2,50 ) + 1,00 crumble: ( 3,50 ) + 0,50 Result: 4,00
There is no connection between those components. That’s good on one hand, because you simply can add new components and it will have no technical impact on the existing classes.
What are the disadvantages?
In my opinion the biggest disadvantage is, that the use case for this pattern is very limited. Another sensible question could be about the weight of the ice cream. That also can be answered very well equivalent to how to calculate the price. Querying a price and the weight looks quite reasonable, but what if I want to know more about the configuration? For example is there a component that has artificial sweeter? How many scoops of ice cream do I have? Do I need to get a bigger waffle cone?
If I think about SAP programming, there is no task where I would find the decorator pattern helpful. There is an example by Naimesh Patel on his blog, but in my opinion its no decorator. The pattern itself is of course the decorator pattern, but the use case does not really fit. Why should I use a quite complex pattern like the decorator for producing different outputs?
There are many many examples for how to use the decorator. Most of them are like:
- making a coffee
- decorating a pizza
- selling ice cream (see above)
in order to have a total price afterwards.
Paul Hardy already wrote 2013 about decorating a hotel, but I didn’t manage to reprogram his examples. additionally it is an example you will not find in an SAP system.
On his home page, Philipp Hauer also describes the decorator pattern very detailed but again fiction examples… Very detailed information about the pattern can also be found on this page: Configuring pizza.
Things I would always do with some kind of a structured table because creating classes for these options is not common. There was one example
Configuring A Car
Because of the lack of a better SAP-world example, I decided to use cars, because this is something most people do know about. Cars are quite common and everyone knows some special options cars have.So I decided to decorate a car with some options. Having at least one well known thing in a world of new makes it easier to understand. Hopefully.
In my example program there is a basic model class (“basic”)and there are some option classes:
Of course there is the main decorator class “option” which is defined abstract, because this class itself will not be used.
The class “basic” will be derived from “option”. “Basic” is the main class for holding the basic car model.
The decorator class “option_decorator” will also be derived from the abstract “option” class but has the main feature of the decorator pattern: a private attribute for holding the predecessor option.
Class “option” (abstract)
"=== abstract option class providing needed methods CLASS option DEFINITION ABSTRACT. PUBLIC SECTION. TYPES: BEGIN OF ts_option, name TYPE string, price TYPE i, END OF ts_option, tt_options TYPE STANDARD TABLE OF ts_option WITH DEFAULT KEY. METHODS get_configuration ABSTRACT RETURNING VALUE(options) TYPE tt_options. METHODS get_price ABSTRACT RETURNING VALUE(price) TYPE i. DATA price TYPE i. DATA name TYPE string. ENDCLASS.
"=== main class of "option" - this is the "basic model" which will be decorated CLASS basic DEFINITION INHERITING FROM option. PUBLIC SECTION. METHODS get_configuration REDEFINITION. METHODS get_price REDEFINITION. METHODS constructor. PROTECTED SECTION. ENDCLASS. CLASS basic IMPLEMENTATION. METHOD get_configuration. APPEND VALUE #( name = name price = price ) TO options. ENDMETHOD. METHOD constructor. super->constructor( ). price = 15000. name = 'Basic Model '. ENDMETHOD. METHOD get_price. price = me->price. ENDMETHOD. ENDCLASS.
"=== Option decorator - this class will handle the option pattern CLASS option_decorator DEFINITION INHERITING FROM option. PUBLIC SECTION. METHODS constructor IMPORTING im_decorator TYPE REF TO option. METHODS get_configuration REDEFINITION. METHODS get_price REDEFINITION. PRIVATE SECTION. DATA lo_decorator TYPE REF TO option. ENDCLASS. CLASS option_decorator IMPLEMENTATION. METHOD constructor. super->constructor( ). me->lo_decorator = im_decorator. ENDMETHOD. METHOD get_configuration. CHECK lo_decorator IS BOUND. options = lo_decorator->get_configuration( ). ENDMETHOD. METHOD get_price. price = lo_decorator->get_price( ) + me->price. ENDMETHOD. ENDCLASS.
"=== OPTION metallic paint CLASS option_metallic DEFINITION INHERITING FROM option_decorator. PUBLIC SECTION. METHODS constructor IMPORTING im_decorator TYPE REF TO option. METHODS get_configuration REDEFINITION. METHODS get_price REDEFINITION. ENDCLASS. CLASS option_metallic IMPLEMENTATION. METHOD constructor. super->constructor( im_decorator ). price = 500. name = 'metallic paint'. ENDMETHOD. METHOD get_configuration. options = super->get_configuration( ). APPEND VALUE #( name = name price = price ) TO options. ENDMETHOD. METHOD get_price. price = super->get_price( ). ENDMETHOD. ENDCLASS. "=== OPTION multimedia system CLASS option_multimedia DEFINITION INHERITING FROM option_decorator. PUBLIC SECTION. METHODS constructor IMPORTING im_decorator TYPE REF TO option. METHODS get_configuration REDEFINITION. METHODS get_price REDEFINITION. ENDCLASS. CLASS option_multimedia IMPLEMENTATION. METHOD constructor. super->constructor( im_decorator ). price = 1000. name = 'multimedia entertainment system'. ENDMETHOD. METHOD get_configuration. options = super->get_configuration( ). APPEND VALUE #( name = name price = price ) TO options. ENDMETHOD. METHOD get_price. price = super->get_price( ). ENDMETHOD. ENDCLASS. "=== OPTION automatic gear CLASS option_automatic DEFINITION INHERITING FROM option_decorator. PUBLIC SECTION. METHODS constructor IMPORTING im_decorator TYPE REF TO option. METHODS get_configuration REDEFINITION. METHODS get_price REDEFINITION. ENDCLASS. CLASS option_automatic IMPLEMENTATION. METHOD constructor. super->constructor( im_decorator ). price = 2000. name = 'automatic gear'. ENDMETHOD. METHOD get_configuration. options = super->get_configuration( ). APPEND VALUE #( name = name price = price ) TO options. ENDMETHOD. METHOD get_price. price = super->get_price( ). ENDMETHOD. ENDCLASS. "=== OPTION rallye stripes CLASS option_rallye DEFINITION INHERITING FROM option_decorator. PUBLIC SECTION. METHODS constructor IMPORTING im_decorator TYPE REF TO option. METHODS get_configuration REDEFINITION. METHODS get_price REDEFINITION. ENDCLASS. CLASS option_rallye IMPLEMENTATION. METHOD constructor. super->constructor( im_decorator ). price = 100. name = 'rallye stripes'. ENDMETHOD. METHOD get_configuration. options = super->get_configuration( ). APPEND VALUE #( name = name price = price ) TO options. ENDMETHOD. METHOD get_price. price = super->get_price( ). ENDMETHOD. ENDCLASS.
There are two methods in the option class:
Get_price will ask the predecessor for its price and adds its own price. The options own price is defined in the CONSTRUCTOR.
The method get_configuration asks the predecessor for its configuration and appends it’s own configuration (name) to the returning table.
The report has four checkboxes respecting one of the four options. When beeing executed, the main object will be created: the “basic” class. For each selected option, the corresponding class will be created. The predecessor object (in case its the first option, the predecessor is the basic model), will be passed as importing parameter of the constructor.
START-OF-SELECTION. "options selection PARAMETERS p_mmdia AS CHECKBOX DEFAULT 'X'. PARAMETERS p_autom AS CHECKBOX DEFAULT 'X'. PARAMETERS p_metlc AS CHECKBOX DEFAULT 'X'. PARAMETERS p_rally AS CHECKBOX DEFAULT 'X'. DATA go_decorator TYPE REF TO option. DATA go_predecessor TYPE REF TO option. CREATE OBJECT go_decorator TYPE basic. go_predecessor = go_decorator. IF p_mmdia IS NOT INITIAL. CREATE OBJECT go_decorator TYPE option_multimedia EXPORTING im_decorator = go_predecessor. go_predecessor = go_decorator. ENDIF. IF p_autom IS NOT INITIAL. CREATE OBJECT go_decorator TYPE option_automatic EXPORTING im_decorator = go_predecessor. go_predecessor = go_decorator. ENDIF. IF p_metlc IS NOT INITIAL. CREATE OBJECT go_decorator TYPE option_metallic EXPORTING im_decorator = go_predecessor. go_predecessor = go_decorator. ENDIF. IF p_rally IS NOT INITIAL. CREATE OBJECT go_decorator TYPE option_rallye EXPORTING im_decorator = go_predecessor. go_predecessor = go_decorator. ENDIF. LOOP AT go_predecessor->get_configuration( ) INTO DATA(option). WRITE: / option-name, 40 option-price. ENDLOOP. . WRITE: / 'TOTAL', AT 40 go_predecessor->get_price( ).
I made some improvements which I just want to mention but will not post due to too much complexity.
First upgrade: The decorator pattern itself has one quirk I really do not like: Creating an object with passing the current object and then overwriting the current object seems quite weird to me. So I tried to hide this in a helper class.
The second improvement is that in this version each option class has the same coding in get_price and get_configuration: Calling the previous object and adding own data to the result. I thought that this should be done in another way. So I derived another class “option_decorator_easy” from “option_decorator” to implement the two methods. All other option classes inherit from this new “easy” class. It makes the pattern even more complex but reduces code implementations.
I also tried to cancel the decoration if a price limit has been reached. Maybe I didn’t implement this the right way, but the way I did has the follwing issue: Each option just adds itself to the configuration. The configuration itself is called at the end, after all decorators have done their job. So I only had the chance to cancel the output of the options in get_price.
For me the decorator pattern is one of the most complicated pattern, because I find it hard to adapt this pattern to the SAP world. The pattern itself is quite complex. I don’t want to think about how this looks like if there really is some functionality in the classes and if additionally interfaces are used…
In the SAP system there are some “decorator” classes. One for unit tests (CL_AUNIT_TEST_CLASS_DECORATOR). Even though I just keep me busy hacking the unit test world (one, two and three) I do not understand how this decorator is used or what it can be used for.
Even if I could think about “decorating” a sales order (transport costs, dangerous goods issue, packing) I instantly see that the decorator is not the pattern that should be used. In most cases I want to interact with the options (if dangerous goods, the packing might be different, if transport by ship, the packing might be different, when packing the items I need to know about the size and so on).
If you have any SAP world examples for the decorator pattern I would be glad to hear!
I am happy for any suggestion, proposal or any other feedback about the post or the decorator and its use.
PS: sorry, no pictures… 🙁
I'd also like to see a real SAP world example.
One example where I like to use the decorator (or maybe a slighty adapted version of it) is for logging or tracing – hopefully that counts for a real world example in SAP.
The key point for me is the interface which helps to switch fast and on exactly one point between the raw functionality and the extended functionality – in the example below between logging and not logging.
Another example which comes into my mind is to calculate the weight of packaged products.
The class for the product will probably know the weight of itself. If the weight for the product wrapped with the packaging material for shipping is needed an separeted decorator class “product_with_package” has the advantage that no adaption on the product class is needed.
*) Image changed
Thanks, Andreas, for this yes-I-would-say-this-is-a-real-world-example-example!
As the logger does not have a "real" connection to the "car", it can only log "car has been built". There is no "insight-logging" possible, what I would prefer because I want to know about what's happening when building the car...?!
Five years ago what I was trying to do was go through all the Java examples in “Head First Design Patterns” and re-write them in ABAP.
They had an example of the decorator pattern trying to build up the price of a cup of coffee. In my hotel example I was just playing around trying to combine disparate types of obects e.g. swimming pool, hotel bar etc.. based on a hotel I visited in Moscow which had a car showroom inside it.
Back in reality I encountered a situation where I could not use filter BADIs for some reason (I cannot remember why).
The problem was that during a business process certain logic should only be applied for certain countries, and certain logic for certain product lines, and it kept getting more and more complicated.
I did not want to have a subclass explosion, so the inhertitance tree was only one level deep (usually). Some subclasses dealt with country specific logic, some with product line secific logic, and some not yet created subclasses will deal with whatever crazy logic is thrown at me.
My factory class gets a structure based into it using “context” data which is just country and product line at the moment, but might (almost 99% sure it will) get extra fields added in the future.
I have a comment in the “returned decorated object” method of the factory as follows:-
* Get Sub Classes
* The list of subclasses comes out such that children of children
* come at the end.
* This is fine for the decorator pattern. The base class is at the
* centre, in the same way that a Christmas tree is at the centre of
* all the decorations that get added to it
* Put another way, the base class is added first, and then wrapped
* with any relevant subclasses
and then a bit later on…
* Using the decorator pattern, if RMC and BE are passed in, we want…
* The base class on the inside
* Then any RMC specific subclasses
* Then any BE specific subclasses
* Then the RMC/BE subclass on the outside
RMC stands for ready mixed concrete, by the way, and BE is Belgium.
The model class has what I call “exit” methods and they call the equivalent method in the predecssor and then do their own thing.
As Enno alluded to it is horrendously complicated, but it works like a charm. The calling program passes in the context data to the factory and gets back an object and the caller does not know or care if that object instance has been “decorated” or not.
I can also create a new subclass without changing either the calling program or the factory.
As I said the filter BADI concept works in pretty much the exact same way, you get an arbitray number of classes with the same interface,all doing their own thing, I just wish I could remember why I could not use it in this case.
I skimmed your post at first reading and wondered why a Russian Hotel would offer ready mixed concrete to guests.
Thanks for your reply, Paul! I understood that you wrapped some specified classes (country and product line) to a base class. The base class provides some functionality; the "subclasses" provide some extra functionality. But how dow you get to this extra functionality? The caller does not know about the decorations. So the added functionality must have an impact to the base class. but how?
I’m with you on this one, have never seen the point of the decorator. To me it looks like ‘sideways subclassing’, neither composition nor inheritance, but fares badly at trying to be both because to me the complexity negates the benefit.
I understand the concept, but have always managed with composition-based solutions such as tables of interfaces. Example:
Note, greatly simplified and contrived to match your example. Perhaps I’d use a builder class to handle merging of options with car but this is just a quick example.
Basically I'm applying 'composition over inheritance' to the decorator pattern 🙂
According to this article https://dzone.com/articles/is-inheritance-dead the decorator is applied through composition.
Yes, he starts off with a pure composite decorator, then describes a drawback that is resolved by a ‘workaround’ which involves inheriting from an abstract decorator class, at which point he’s essentially is back to the ‘classical’ decorator pattern that uses inheritance.
Emperor’s new clothes I say
Only one inheritance though to address a specific drawback as the pattern is implemented in Java (And would be in ABAP). Not really classic inheritance.
Are we not just moving the inheritance from the functional class to the pattern implementer and making the caller do more work? To me the hierarchy and all the passthrough methods are still unnecessary complexity.
Perhaps because ABAP is so heavily data- and table- oriented, it is an ABAP-specific thing that this kind of processing is easier to implement in linear fashion than the semi-recursion that the decorator resembles.
I've just never made friends with it. To me it is too complex for the (ABAP?) problems it tries to solve and I have always come up with a simpler way in the scenarios I've encountered.
So far, I've not used it. But I don't think it violates the composition over inheritance rule. Even though, for just one level only, it uses inheritance.
I wouldn’t put it quite as harsh as ‘violating the rule’, inheritance has it’s time and place.
But that aside, yes we’ve removed inheritance from the main class, but if we change or add a method to the class, we potentially still need to change all the decorators. To me this is brittle and still retains some of the drawbacks of inheritance. An author of a class may not be aware of all the decorators that may rely on a signature because they are now two levels of where-used-ness away.
A facade or tabular composition construct can be used to a similar effect and is simpler and more robust IMHO. Like Enno, I simply haven’t come across a need in ABAP, but am open to change my mind if someone can present a good use case.
my 2 cents: patterns are concept, not rote. The decorator intent is to attach additional responsibilities to existing code at run-time. I think this is what is done in legacy ABAP when a list of function modules (internal table) with the same interface called dynamically. In ABAP OO I would probably use the BAdI framework, e.g. BAdI with multiple implementations and a default option.
The good thing is, you can implement this logic from scratch (I once did this for a mapping tool). I think we are not discussing this more often because the original developer often does not need this flexibility. It is added complexity that is only (extremely) helpful for the maintenance guys.
Here a picture generated from your code:
I think BAdI is not really the best way to construct a decorator. Most of the time you need an order, e.g. you need to to do security, validation and logging. You can create an order with BAdI BADI_SORTER, but I found this quite cumbersome.
Best regards, Tapio
If the original programmer does not need the complexity of decorator, then he/she shouldn't put it in - in accordance with the least work principle of doing exactly what is needed. A later programmer wanting to add functionality will then refactor the original code so that it does use the decorator pattern (if that's appropriate!).
I would assume, it's mostly not. Refactoring a could to using a decorator is definitely not a think I would do or suggest any other to do. Maybe because I am not really aware of how the decorator works.
Plus: I think this would break any existing unit test.
But: your intention of course is right: Do what is required and not more.
interesting. I use the decorator pattern very often. I think it is an elegant way to extent functionality without changing existing code. One of the best articles about the decorator pattern I found is this one (don't get confused by the title; great site about software design, by the way).
The decorator pattern also combines with other patterns quite well. Here is an example of decorator pattern combined with command pattern.
I am not surprised by your statement that you use the decorator quite often, as you are one of the really oo experts I know... 😉
Thanks for your example. Adding caching mechanism to a class sound really real-world and promising! Here I can understand how the base and the decorator are related, because the cache-decorator does not need to know anything about the class (or instances) it's caching and vice versa.
I wil need some minutes to understand your command decorator example. Thanks for this example!
Meanwhile I think the thing I struggle with most is the idea of what could be the real additional functionality.
A few years ago, I wrote a file handling class. Initially it was used only to read CSV files from the presentation server, but later, through refactoring and subclasses, I adjusted it to read xlsx files, the clipboard, and the appserver.
It seems to me now, that I could achieve this better by using the decorator.
Just off the top of my head, I need decorators for
In the opinion of the group - would this be the right approach? Is it workable?
Off the top of my head, my approach would be a file meta-class and source (stream?) and interpreter interfaces implemented by a bunch of concrete classes.
This is obviously just a quick idea. With a real implementation it would include a lot more error handling, abstract factories, less chaining; and throughout implementation would be refactored into something completely different
Still, the basic idea is a workable start for something that contains neither decorator nor inheritance.
If you want that for client there were only one class you could use that approach.
In that case I would use factory method for every type of file or Chain of Responsibility pattern.
The starting point would be something like
It is workable, but it would only be the right approach if the various implementation can mix (do they? e.g. xslx from the clipboard?). If not, it is overkill: state/strategy comes to mind.
Decorator is a mix of objectify and object recursion, so we expect to execute more than one concrete decorator implementation at run-time. This is why the standard example is pricing, where we execute all price conditions routines to get the final price. We do not need to re-invent this wheel in ABAP.
That's one of the often mentioned examples for a decorator. But the decorator pattern seems to be oversized for me. I see no benefit to a specified class that simply uses the base object as "input" to get the data from it.
Thanks to all for your comments and interest in this topic!
I think when I really understand this pattern, I moved one level up in oo thinking…
Next thing I will concentrate on is “caching with the decorator” which now comes very close to my understanding of the decorator and promises great advantages for data access objects. Normally the caching is provided within the class what makes the class more complex and you will have to test not only the class itselft but also the caching mechanism. If there should be a way of having a universal decorator for this then this would be great.
Keep decorating your life and code
For me this discussion exactly shows the main points about patterns:
For me patterns are a structured and accepted way for "dynamic programming in ABAP"!
I love dynamic solutions with interfaces, "Class/Method Naming Conventions", activating functionality via customizing,... even with RTTI ;), and some of my colleagues really hated my for this!
But most of these implementations follow an "official design pattern", and therefor I just had to explain these patterns once and tons of coding was understandable!
And to provide a real-world-example: I need to fill some custom IDoc segments of CRMXIF_PARTNER_SAVE01.
I changed this coding today in the morning - guess the pattern 😉
1 interface and 3 classes later:
I don't feel adding a new class to the ITab will violate the OpenClosePrinciple, so for me this is a simple way to allow enhancements but with some control and documented (the main BADI method is recored in a transport request!)
thanks for your additions and suggestions!
That's exactly what my colleage tells me each time I ask him about design patterns... 😉 And this is exactly the main difficulty IMHO. When learning Design Patterns you must go the way of learning the names and their behaviour. After that you can decide by "behaviour" knowing that there is a specific name for it...