We all know that we can’t redefine Static Method. We’ll try to explore the reasoning and alternatives to achieve the similar behavior.
Redefinition is to over-ride the default implementation of the method in the subclass. This provides us the degree of polymorphism.
Reason – we can’t override Static Method
As we have seen in Overriding or Redefinition, it allows us to change the behavior of the instance method at runtime as long as we have redefined the method. At compile time, we use the Super Class to define the object. But which implementation system is going to take, would be only known at runtime. So, if have redefined the method, that implementation would be executed. But if we haven’t, than it would take implementation from Superclass.
Now, for static methods there isn’t any unknown factors at compile time. The method which we are trying to execute, it would be executed at the runtime as Static methods are independent of the instance components. That the reason we can’t use the instance attributes within static methods without using the instance of the class. Static methods are operable on their own.
With static methods, you call them by explicitly specifying the type on which they are defined. Which means, you directly call the implementation, which is not bound to any specific implementation of the instance object. This would be the reason, they can’t be overridden.
Alternatives
Lets say we have a logger method ZCL_LOG which we are using to log various stuff. Currently, this logger method only write to Application Log. Based on further requirement, we come to a point that we need to now log into a separate file or into DB table or an application log. Since we have the method as static we can’t just override that make it work.
ZCL_LOG static method
* CLASS zcl_log DEFINITION. PUBLIC SECTION. CLASS-METHODS: log. ENDCLASS. "zcl_log DEFINITION * CLASS zcl_log IMPLEMENTATION. METHOD log. * write into Application Log ENDMETHOD. "log ENDCLASS. "zcl_log IMPLEMENTATION
So here is the possible solution to avoid this situation.
Singleton + Helper Class
We will move out of the Static method. We’ll use method as a common interface which everyone can use to Log but we’ll create helper classes to actually do the logging.
* ZCL_LOG=>LOG changes – Add an importing parameter to decide Logger. Change the implementation to use the actual logger object to write into Log.
* Logger Class hierarchy – Create ZCL_LOGGER which can be inherited for individual Logging processing. CREATE_OBJECT is the method for Singleton implementation of the object.
The idea behind this is we can get multiple requests to write the log. So, we can call the ZCL_LOG=>LOG in many times for the same log type. Thus we need to implement Singleton. We can even enhance the singleton implementation to save more than one instance – one for each log type. Please read the comments for more clarification.
Log Class Hierarchy
* CLASS zcl_log DEFINITION. PUBLIC SECTION. CLASS-METHODS: log IMPORTING log_type TYPE char1. ENDCLASS. "zcl_log DEFINITION * CLASS zcl_log IMPLEMENTATION. METHOD log. DATA: lo_log TYPE REF TO zcl_logger. lo_log = zcl_logger=>get_object( log_type ). lo_log->write_log( logging_msg ). ENDMETHOD. "log ENDCLASS. "zcl_log IMPLEMENTATION * CLASS zcl_logger DEFINITION ABSTRACT . PUBLIC SECTION. METHODS: write_log ABSTRACT IMPORTING iv_log TYPE string. CLASS-METHODS: get_object " Singleton method IMPORTING IV_LOG_TYPE TYPE CHAR1 RETURNING value(ro_obj) TYPE REF TO zcl_logger. PRIVATE SECTION. CLASS-DATA: O_LOG TYPE REF TO ZCL_LOGGER. CLASS-DATA: V_SAVED_LOG_TYPE TYPE CHAR1. ENDCLASS. "zcl_logger DEFINITION * CLASS zcl_logger implementation. method get_object. IF v_saved_log_type NE iv_log_type. CASE iv_log_type. WHEN 'F'. lcl_class = 'ZCL_LOG_TO_FILE'. WHEN 'D'. * DB Logger WHEN OTHERS. * Application log - default ENDCASE. CREATE OBJECT ro_log TYPE ref to (lcl_class). o_log = ro_log. v_saved_log_type = iv_log_type. ELSE. ro_log = o_log. ENDIF. endmethod. endclass. * CLASS zcl_log_to_file DEFINITION INHERITING FROM zcl_logger. PUBLIC SECTION. METHODS: write_log REDEFINITION. ENDCLASS. "zcl_log_to_file DEFINITION * CLASS zcl_log_to_file IMPLEMENTATION. METHOD write_log. ENDMETHOD. "write_log ENDCLASS. "zcl_log_to_file IMPLEMENTATION
Conclusion
This boil downs to the fact that Static methods should be only used for Singleton Implementation and some time Utility class e.g. methods is CL_GUI_FRONTEND_SERVICES.
Hello Naimesh,
Actually STATIC components(methods & attributes) are specific to the class where they are defined. Hence defined as “CLASS”-METHODS or “CLASS”-DATA.
And since the STATIC methods belong to the class in which they are defined, they cannot be re-defined(or overridden) in the sub-classes. STATIC methods thus have the same implementation across the class hierarchy.
BR,
Suhas
Hello Suhas,
CLASS addition to define DATA & METHODS static components in the ABAP. Other languages use STATIC addition to define static component. CLASS addition does let us know that they are STATIC but, that doesn’t provide information on why it wouldn’t be redefined.
Regards,
Naimesh Patel
Hello Naimesh,
You shouldn’t take the “CLASS”-METHOD reasoning too literally 🙂
Rather think STATIC method implementation to be class-specific & not instance-specific. So, if the implementation is class-specific you cannot have a different implementation in a different classes(read: sub-classes).
BR,
Suhas
PS: This reasoning is provided by James Wood in his OO-ABAP book & i kind of like this school of thought.
Sure, Suhas.
I guess, we both are saying the same thing.
Regards,
Naimesh Patel
Hello Naimesh,
I have some doubts here.
1. I think you are discussing case where user wants to have one log type per each session. Suppose user is calling ZCL_LOG=>LOG( ‘F’ ) to create FILE log. With singleton we would create ZCL_LOG_TO_FILE object and store it inside ZLC_LOGGER. Now what happens if user wants to change log type by calling ZCL_LOG=>LOG( ‘D’ ). The object that GET_OBJECT returns would be of type ZCL_LOG_TO_FILE and not ZCL_LOG_TO_DB.
So the first call would be decisive here. Once log type chosen it couldn’t be changed anymore. I therefore wonder if we need this singleton here. If not, we can create new log type in LOG method everytime it’s recalled. So the implementation would be.
METHOD log.
DATA: lo_log TYPE REF TO zcl_logger.
CASE log_type.
WHEN ‘F’.
CREATE OBJECT lo_log TYPE zcl_log_to_file.
WHEN ‘D’.
CREATE OBJECT lo_log TYPE zcl_log_to_db.
WHEN OTHERS.
CREATE OBJECT lo_log TYPE zcl_log_appl.
ENDCASE.
lo_log->write_log( logging_msg ).
ENDMETHOD.
2.If we wanted to use static GET_OBJECT method of ZCL_LOGGER we should decide right there what object type it must return (not at LOG method level). So the parameter from LOG should be passed to GET_OBJECT in order this one decides of real object type. Otherwise how GET_OBJECT would know which object type it must create and return to LOG method. So I think we don’t actually need this static method either.
Assuming all of this I would strive towards above impl. and giving up singleton unless I am missing something, which is likely. Awaiting your comments 🙂
Cheers
marcin
Hello Marcin,
I guess, you are thinking that logging_msg would be a table containing ALL of the log, where as I had loggin_msg as a single message in mind. It would had been better if I had given all the implementation.
Using class zcl_log_to_file, we’ll write Log into the file each at a time. So, WRITE_LOG method will only do the append to some internal table which is an attribute of the ZCL_LOG_TO_FILE class. Since we want to keep on appending the data into the same internal table, we need to implement Singleton which would be done by CREATE_OBJECT method.
CREATE_OBJECT method could have two different implementations.
We could similarly create a ITAB which would contain all the object with Log type. CRAETE_OBJECT can get the required object – Create if its not there, get from ITAB if its there.
While writing this I realized that the implementation of the LOG method is incorrect. It should be:
Thanks for pointing this out. I’ll correct the code based on our discussion.
Regards,
Naimesh Patel
Hi Naimesh,
If we treat log types as separate streams which can be only opened once for writting, then this approach is of course correct. As you noticed I was missing this aspect.
I would also see this working with a bunch of loggers stored in a table (as you suggested) which could be either read from or inserted to table keeping their uniqueness. If there was only one reference in ZCL_LOGGER for storing our single instance, we would keep overwriting it after each log type change (if V_SAVED_LOG_TYPE ne IV_LOG_TYPE). So the singleton wouldn’t work.
Overally I like the concept 🙂
Regards
Marcin