OO geek journal – hiding method invocation
Few days ago I was facing a problem, where I needed to use some common algorithm which differs only in some small aspect, that is delegating some task to different methods. Please consider below academic version of a case.
Quick jump in
Model class
This simple class has merely three methods, get_all, get_active, get_inactive. All they do is to return some set of components. Now, let’s create simple Controller class which utilizes model methods to show the results.
Controller class
The important part is that for some reason in each method we need to repeat model creation. Let’s assume this is very complex process which relies on different factors, which we should not bother here. Each method however does the same thing, but it only differantiates model’s method invocation. One time it calls get_all, another time get_active or get_inactive. This is the only part which differs all three controller methods, but for some reason must are tied to the code embracing it.
Test program
Result
All:
01 COMP1
02 COMP2
03 COMP3
Active:
01 COMP1
Inactive
02 COMP2
03 COMP3
Redundancy
As we noticed above we have redundant code which does exactly the same with small difference of model methods’ call. What we wish to have instead is one common method which only differs method call based on some parameter. Using IFs or CASE is ugly practice. We will therefore utilize Object Orientation.
wish to have
Hiding method invocation
First we need to introduce new class, whose subclasses represent different component’s sets. It has only one method get_comp which returns different sets of components in different subclasses.
Abstract class
Subclasses
All three classes simply receive model instance and use them appropriately to their meaning.
As simple as that
Now all we need is our common method in the controller which hides model methods invocation by utilizing interface (abstract class).
new Controller definition
uniform get_comp method
Notice that get_comp represent redundant code and is no longer aware of which method we want to call. It even doesn’t know which object is it working with. The only information it posses is that this object is of type lcl_model_comp (abstract type). So in fact we are programming to an interface as at the time of program creation we don’t know which class we are working with.
We leave the decision of method selection to the specific instance of lcl_model_comp, be it either lcl_model_comp_all, lcl_model_comp_active or lcl_model_comp_inactive. The last thing to do in order get_comp works correctly is to call it with different lcl_model_comp instances in the appropriate methods.
Please don’t bother the fact that showing the result is implemented in all the methods. We could of coruse move that to other method which only shows what was returned. What I wanted to show here is that the result lt_comp originates from get_comp depending on model_comp object we choose.
Conclusion
Sometimes it may be required to invoke different methods of same object inside some complex algorithm. By utilizing simple characteristic of Object Orientation – that is object diversity, we are able to create more uniform code which eliminates redundancy. By introducing another level of abstraction (lcl_model_comp) we don’t need to worry anymore of the method selection at its invocation place. Instead we move that responsibility to that new class instance.
Hello Marcin,
wouldn't it be easier to use a static factory method in lcl_controller instead? Therein create the instance for lcl_controller and lcl_model and you'll have the same effect, only one place to create a lcl_model instance.
I don't see the need of using abstract class, inheritance and polymorphism in this special case, anyhow they might be great tools in other cases.
best regards
Thomas
Hello Thomas,
I must disagree here. Using factory method we would only hide model object creation, which of course was part of the problem. But we still leave w/ an issue of different methods call.
In my blog I wanted point on how we can encapsulate different methods call, where we leave the decision their selection indirectly to the callers (show_.. methods). In fact these callers could be represented by the Client, so we would end up with only one method (get_comp) and the Client would make the choice of model_comp selection, hence calling different methods (either get_active, get_inactive or get_all). Such technique is know as double dispatch.
Regards
Marcin
Hello Marcin,
yes you're right. My answer was to quick. Didn't see the whole picture of what you was figuring out. Sorry 🙂
regards
Thomas
Hello Thomas,
No problem. This only made me thinking if I did proper design steps 🙂
Regards
Marcin
Hello Marcin,
thanks for taking the time.
Real life problems usually take a lot of time to analyze. Your academic version is well presented and I had fun following it. I found myself drawing the class diagram a checking which refactoring you applied.
Now time to nit-pick, my comments:
it seems to me the uniform get_comp( ) method should depend on lcl_model_comp abstract class and not on the concrete lcl_model class. The code should not be able to just create the object if the parameter model is not bound. So basically you should remove lcl_model from your code.
I would propose the following refactoring to merge the 3 show_...( ) method into one that would be called 3 time with a parameter
with this, the show method would look like
lo_model = factory( c_all ). " or c_active or c_inactive
display( lo_model ).
the single display( ) method would do
lt_comp = lo_model->get_comp( ).
WRITE lo_model->get_text( ).
LOOP AT lt_comp..
WRITE ..
ENDLOOP.
My understanding is that you are applying the state/strategy pattern with a single dispatch, no double dispatch.
thanks again for taking the time,
regards,
JNN
Hello Jacques,
Thank you for nice words and sharing your thoughts, here are mine 🙂 :
1. actually get_comp is using abstract lcl_model_comp, not concrete class, so not sure what you mean here.
2. your factory method would rely on parameter (contant which indicates which lcl_model_comp object we want to create). I see it like:
case ip_param.
when c_all.
create object rr_model_comp type lcl_model_comp_all.
when c_active.
create object rr_model_comp type lcl_model_comp_active.
when c_inactive.
create object rr_model_comp type lcl_model_comp_inactive.
endcase.
In such case when new "state" arrive, you would need to change factory method implementation, that is you violate "open-closed" principle. In my example when you need to add new "state" you:
a) implement new class which subclasses lcl_model_comp and call this new state method in get_comp
b) add new show method (or add new client call) which simply utilizes this new class object
With these all you do is "add something" to the implementation, not "change something".
3. I agree to introduce additional method get_text which will represent lcl_model_comp subclass "state" in words
4. Actually it's close to state pattern, but I can't agree on single dispatch. Normally, as seen in initial example, we had single dispatch
method show_active.
lo_model->get_active( ). "here dispatch happens
endmethod.
Now, we have double dispatch:
method get_comp. "in lcl_controller
lo_model_comp->get_comp( ). "first dispatch
endmethod.
method get_comp. "i.e. in lcl_model_comp_active
lo_model->get_active( ). "second dispatch
endmethod.
Any commets welcome, maybe I missed something too 🙂
Regards
Marcin
Hello Marcin,
oops I got it all wrong 😕 . You are actually implementing a visitor ?
I will go back to code and return only after more study.
Anyway it is still fun, thanks.
Hello Jacques,
I did not face visitor pattern yet, so I need to study it first to claim whether I am using it or not 🙂 For me it was just intention of sharing idea of how to get over some basic issue. Did not focus on design pattern even I accidentaly had used one.
BTW: oops for me only means "Object Oriented Programming Sanity" not your mistake 🙂
Cheers
Marcin