03-22-2021 10:52 AM
Hello experts,
When writing a class, I can define the visibility of a method. When implementing an interface in a class I cannot define the visibility. Foe encapsulation purpose I want to define some of the methods as private. (They are not supposed to be shown to the outside world). How can I achive it? I read that, in a class I can define an alias to the interface's method and define it as private. That does not solve the problem because from the outside world (from a program) I still can relate to the interface's method. (I cannot relate to the alias).
For example
Class zcl_try implements zif_try interface. The interface has two methods. One (method1) supposes to be private and the other (call_method1) suppose to be public (and called from a program). I create an alias method1 for zif_try~method1 with visibility - private.
class ZCL_TRY definition
public
final
create public .
public section.
interfaces ZIF_TRY .
protected section.
private section.
aliases METHOD1
for ZIF_TRY~METHOD1 .
ENDCLASS.
CLASS ZCL_TRY IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_TRY->ZIF_TRY~CALL_METHOD1
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method ZIF_TRY~CALL_METHOD1.
ME->method1( ABAP_TRUE ).
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_TRY->ZIF_TRY~METHOD1
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_1 TYPE BOOLEAN
* +--------------------------------------------------------------------------------------</SIGNATURE>
method ZIF_TRY~METHOD1.
MESSAGE 'ZIF_TRY~METHOD1' TYPE 'I'.
endmethod.
ENDCLASS.
In a program I still can referrer to method1
data:lif TYPE ref to zif_try.
lif = new zcl_try( ).
lif->method1( abap_true ).
Thanks in advance
Hagit
03-22-2021 12:46 PM
Hi!
An interface defines the "interface" used for "something" that can be implemented by different classes. One could say it defines the "public API". For instance that there is a specific data type and specific methods that can be called.
The interface would not define HOW the class or classes implements the interface, just how it is to be used, and therefore not private methods or data is relevant in the interface and can not be defined there.
Private methods, types and attributes are part of the inner workings of an implementation that should NOT be forced by an interface definition.
You can, and should, still define private methods in the class that implements the interface, but they should not be in the interface.
03-22-2021 12:46 PM
Hi!
An interface defines the "interface" used for "something" that can be implemented by different classes. One could say it defines the "public API". For instance that there is a specific data type and specific methods that can be called.
The interface would not define HOW the class or classes implements the interface, just how it is to be used, and therefore not private methods or data is relevant in the interface and can not be defined there.
Private methods, types and attributes are part of the inner workings of an implementation that should NOT be forced by an interface definition.
You can, and should, still define private methods in the class that implements the interface, but they should not be in the interface.
03-22-2021 1:20 PM
Exactly what Jörgen says.
I suspect where the OP says it's "for encapsulation purposes" it means that the same bit of code (or similar methods) is used in different places. Then the solution should then be:
03-22-2021 1:47 PM
jorgen_lindqvist41 Thanks for your answer.
The scenario is:
There is a method (is_field_ok), which returns if a field is valid. This method calls another method (sel_from_tbl), which returns data that helps to decide if the field is valid.
I thought that I should put both methods in interface (zif_validate)
I create class zcl_validate, which implements the interface (2 methods)
In two other classes I create attribute(lo_validate) ref to zif_validate, and call lo->is_field_ok. Here I do not have to call method sel_from_db. (The method is for internal use of zcl_validate). Therefore I want it to be private.
Do you say that:
Thanks
Hagit
03-22-2021 2:56 PM
One option is to answer yes to your questions 1-3 there.
But if the method sel_from_tbl is the same and used in many places, it probably belongs in a class of its own, that can be instantiated and called from those other classes. There can very well be an interface for that one as well.
03-22-2021 3:47 PM
jorgen_lindqvist41 thanks for your answer.
You wrote: "But if the method sel_from_tbl is the same and used in many places"
Yes, it is.
Every zcl_valdate_x (zcl_validate, zcl_validate_2, zcl_validate_3) should implement method sel_from_db. Is ts correct to:
thank
03-22-2021 4:22 PM
I would probably, in the best of worlds, do this:
1. create zif_validation_utility with the sel_from_db method (and maybe more) and a class zcl_validation_utility that implements that.
2. In zcl_validate, create a private attribute mo_validation_utility TYPE REF TO zif_validation_utility.
3. In constructor if zcl_validate instantiate the mo_validation_utility = NEW zcl_validation_utility( ).
4. In zcl_validate, usse mo_validation_utility->sel_from_db( ).
Some would argue that it is better to use a factory, than instantiating directly in the constructor. But I'm not there... 🙂
You could do steps 2 and 3 in a zcl_parent_validate and inherit in the zcl_validate as well. I don't typically do that, but it might be good in this case... Or is that giving us additional dependencies?
Let's tag matthew.billingham and frdric.girod here 🙂
03-22-2021 5:42 PM
I'm still agreeing with Jörgen. The problem with your sel_from_db is that you're defining the implementation that must be used this to decide if field is ok. For example, you could instead read one of the DD tables to know if the field is valid. Or you might be using CL_SQL for a different database.
As far as factory over direct instantiation - in a 731 system I prefer the former, in 740+ I prefer the latter, because you have new.
So: pre 740
IF zcl_my_class=>factory( )->is_valid( ) eq true.
740+a bit
IF NEW zcl_my_class( )->is_valid( ).
03-23-2021 7:01 AM
Ola,
I like this kind of discussion 🙂
I am not totaly following friends jorgen_lindqvist41 & matthew.billingham, on this part of Abap.
You are exactly at the point, where you should wonder, Is it normal to search each time, I have a rule or whatever, on this table ??
And just imagine, tomorrow the data you would like to retrieve, is not at the same place, maybe you have to get it through Odata. Will you rewrite all your code? (it is your big dependency)
So, what is the issu to have one class that retrieve data, where ever they were ?
The solution (at the beggining) is not very simple to understand, but so powerfull when you have it.
[ Interface or Rule ] --> [Class of Rule (method to get_data) --> (method to instanciate the data) -- ] --> [ Factory for Data ] --> [Interface Data ] --> [ Class of Data ].
(I could write you the corresponding code, but it takes time & I prefer to be sure you are interested before starting 🙂 ).
Interface is a contract between the Class & the outside. (So it could be only public)
The class has to use Data, so there is a method to Get_The_Data, but this method use an instance of the good Class.
To have the instance of the good Class, you have to use a Factory (Factory is here, just to break dependency, it is a very short class)
There is again an Interface, because it is BAD to write Class without Interface
There is finaly the Class of the Data
Good luck
03-24-2021 4:31 PM
jorgen_lindqvist41 , matthew.billingham , frdric.girod thank you for your answers.
frdric.girod I would be happy if you will write the corresponding code. It will help me to understand your answer.
Thanks
Hagit
03-25-2021 7:22 AM
Ok I have wrote a full example with stupid data (very very quickly)
The Interface to get the Data
interface ZIF_DATA_01
public .
types: begin of ts_result_table1,
information_1 type string,
information_3 type string,
information_4 type string,
end of ts_result_table1.
methods get_data_from_table1
importing
information_1 type string
information_3 type string
returning
value(result) type ts_result_table1.
endinterface.
The first class based on this interface, when the data comes from DB
CLASS zcl_data_01_from_db DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
interfaces zif_data_01.
ENDCLASS.
CLASS zcl_data_01_from_db IMPLEMENTATION.
method zif_data_01~get_data_from_table1.
" SELECT blablabla ... but I have not this table
result = value #( information_1 = information_1
information_3 = information_3
information_4 = 'toto' ).
endmethod.
ENDCLASS.
The second class based on the same interface, but ... sometimes I need to get data from Odata
CLASS zcl_data_01_from_odata DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
interfaces zif_data_01.
ENDCLASS.
CLASS zcl_data_01_from_odata IMPLEMENTATION.
method zif_data_01~get_data_from_table1.
" Get blablabla ... but I have not this Odata connection
result = value #( information_1 = information_1
information_3 = information_3
information_4 = 'toto' ).
endmethod.
ENDCLASS.
Now the famous Factory, Technical class, just to avoir dependency between the class Rule & Data
CLASS zcl_data_factory DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
constants data_type_db type string value 'db'.
constants data_type_odata type string value 'odata'.
methods get_data_01_instance
importing
data_type type string
returning
value(result) type ref to zif_data_01.
ENDCLASS.
CLASS zcl_data_factory IMPLEMENTATION.
method get_data_01_instance.
result = switch #( data_type
when data_type_db
then new zcl_data_01_from_db( )
when data_type_odata
then new zcl_data_01_from_odata( ) ).
endmethod.
ENDCLASS.
Now an Interface for the Rule
interface ZIF_RULE_01
public .
methods IS_Rule_1_valid
importing
information_1 type string
information_2 type string
returning
value(result) type abap_bool.
endinterface.
And the class of the Rule
CLASS zcl_rule_01 DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
interfaces ZIF_rule_01.
PRIVATE SECTION.
methods get_data_instance
returning
value(result) type ref to zif_data_01.
data o_data_instance type ref to zif_data_01.
ENDCLASS.
CLASS zcl_rule_01 IMPLEMENTATION.
method zif_rule_01~is_rule_1_valid.
get_data_instance( )->get_data_from_table1( information_1 = information_1
information_3 = 'tutu' ).
endmethod.
method get_data_instance.
result = cond #( when o_data_instance is bound
then o_data_instance
else new zcl_data_factory( )->get_data_01_instance( zcl_data_factory=>data_type_db ) ).
" save instance to not ask each time.
o_data_instance = result.
endmethod.
ENDCLASS.
In my Rule Interface or Class, I have not the exact name of the Class, better, I could select witch kind of instance I would like .. db or odata.
There is only one class know the official name of the class : the factory. Factory is the only class with dependencies, it is her job.
The code looks little bit longger than others. But now, you have at one place, the SELECT statement or the Odata, and you could reuse everywhere. But to be sure to never have to modify the input/output. You should name clearly your method & define the exact input/output parameters corresponding to this method name.
03-25-2021 8:14 AM
frdric.girod thank you for your answer.
You wrote: " here is only one class know the official name of the class : the factory. Factory is the only class with dependencies, it is her job."
What does it change if class zcl_rule_01 knows the official name of the class or knows the name of the constant (data_type_db or data_type_odata). When there is a change in the logic - What does it change if I have to update the name of the constant or have to update the instantiate in the constructor {new zcl_data_01_from_db() or new zcl_data_01_from_odata() }
Thanks
Hagit
03-25-2021 11:34 AM
Why to do this ?
Imagine, you don't use Factory, one year later, these central class works perfectly, you have plenty of them. But now there is a new way to find some data. Because you have for example a central Master Data system. Or maybe you have a big refactorying project, and you need to rewrite some of these class. All the dependencies in all the class/program need to be change. (To change the class name in all these sap objects). If you have a factory, you need to change in only one place: the factory.
it is not a strange idea of an abap developer, it is one of the most known Design Pattern (search on internet)
Second point, have a look to the SOLID principle. The starting point is : What are the things so boring when I need to change a code of somebody else. In front of all these problem, they have imagine 5 main rules named SOLID
03-25-2021 2:17 PM
frdric.girod thanks for your answer.
You wrote: "If you have a factory, you need to change in only one place: the factory."
Lets say that one year later I have to write a new class zcl_data_01_from_xx( ) and use it instead of zcl_data_01_from_db( ). Do you say that the only thing that I would have to do is to replace line 10 by "then new zcl_data_01_from_xx( )"
CLASS zcl_data_factory IMPLEMENTATION.
method get_data_01_instance.
result = switch #( data_type
when data_type_db
then new zcl_data_01_from_db( ) "line 10
when data_type_odata
then new zcl_data_01_from_odata( ) ).
endmethod.
ENDCLASS.
Your second point is not clear. Could you please explain it in more details?
Thanks
03-25-2021 2:47 PM
CLASS zcl_data_factory IMPLEMENTATION.
method get_data_01_instance.
result=switch#( data_type
when data_type_db
then new zcl_data_01_from_db()
when data_type_odata
then new zcl_data_01_from_odata()
when data_type_xx
then new zcl_data_01_from_xx( ) ).
endmethod.
ENDCLASS.
Yep you could replace or you could add directly the new class.
The second point is really complex. It is the beggining of the Clean Code.
A group of developers wonder, why it is so painfull to make correction in programs. And what you discovert is whatever you use for programming, the problem are always the same:
And they have proposed to solve most of these problem with a list of rules, First letters of the word SOLID is a rule:
if you want to understand everything, and write clean ABAP Code, I recommend you this book: object-oriented design with abap
This is THE BOOK to read, after you will never look ABAP as before 😉
03-30-2021 2:34 PM
frdric.girod thanks for your answer.
You wrote:" Yep you could replace or you could add directly the new class."
If I add the new class in line 10,
CLASS zcl_data_factory IMPLEMENTATION.
method get_data_01_instance.
result=switch#( data_type
when data_type_db
then new zcl_data_01_from_db()
when data_type_odata
then new zcl_data_01_from_odata()
when data_type_xx
then new zcl_data_01_from_xx( ) )."line 10
then I must change the constant in line 20 (from data_type_db to data_type_xx)
CLASS zcl_rule_01 IMPLEMENTATION.
. . .
method get_data_instance.
result = cond #( when o_data_instance is bound
then o_data_instance
else new zcl_data_factory( )->get_data_01_instance(zcl_data_factory=>data_type_db ) ). "line 20
So my question stays the same: What does it change if I have to update the name of the constant as above
Or
have to update the instantiate in the constructor of class zcl_rule_01 {new zcl_data_01_from_db() to new zcl_data_01_from_XX() } In case I do not use factory as jorgen_lindqvist41 suggests.
Why is it more dependency to change name of a class than to change name of a constant?
03-22-2021 1:58 PM
Hi,
if you have a method that should not be visible from the outside, is there a particular reason to put in an interface? How does your inheritance tree look like?
03-22-2021 2:26 PM
georgislavov thanks for your answer.
Every class (or at list most of classes), which implements the interface needs also the second method (sel_from_db), therefore I put it also in the interface. Is it incorrect? (I am new to OOP)
You ask about my nheritance tree - I do not use inheritance but composition.
Thanks