Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Make interface's element (method, attribute…) private

hagit
Active Participant

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

1 ACCEPTED SOLUTION

joltdx
Active Contributor

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.

17 REPLIES 17

joltdx
Active Contributor

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.

matt
Active Contributor

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:

  1. Create an interface for the shared stuff.
  2. Create a class implementing that interface.
  3. Instantiate and call methods of that class from the other class.

hagit
Active Participant
0 Kudos

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:

  • 1.Method sel_from_db should not be declared in the interface .
  • 2.Method sel_from_db should be declared in zcl_validate?
  • 3.If there is another class (zcl_validate_2), which implements zif_validate, is the answer to above questions will be the same?

Thanks

Hagit

joltdx
Active Contributor
0 Kudos

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.

hagit
Active Participant
0 Kudos

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:

  • 1.create another class (zcl_parent_validate), with sel_from_db method.
  • 2.zcl_validate, zcl_validate_2, zcl_validate_3 will inherit zcl_parent_validate
  • 3.In zif_validate put all the methods that are needed for validation and must be visible to the outside world
  • 4.Zcl_validate will implement zif_validate and will have private methods, which should not be visible to the outside world.
  • 5.The outside world will instantiate zcl_validate and call the public methods (is_field_ok)

thank

joltdx
Active Contributor

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 🙂

matt
Active Contributor
0 Kudos

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( ).

Ola,

I like this kind of discussion 🙂

I am not totaly following friends jorgen_lindqvist41 & matthew.billingham, on this part of Abap.

  • First point: When I learn Clean Code, I have a rule: If you have a private method you should ask yourself if it could not be a public method of another Class.
  • Second Point, SOLID principle said Single Responsibility. And you violate this rule. You have a class that check Rules & Search Data on DB.

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

hagit
Active Participant
0 Kudos

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

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.

hagit
Active Participant
0 Kudos

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

0 Kudos

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

hagit
Active Participant
0 Kudos

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

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:

  • Variables with stupid name like data or information (like me here)
  • Variables used several time
  • Method doing several thinks at the same time
  • Code call everywhere, and with high dependency.

And they have proposed to solve most of these problem with a list of rules, First letters of the word SOLID is a rule:

  1. Single responsibility
  2. Open–closed
  3. Liskov substitution
  4. Interface segregation
  5. Dependency inversion

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 😉

hagit
Active Participant
0 Kudos

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?

former_member660513
Participant

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?

0 Kudos

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