Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
beyhan_meyrali
Active Contributor
Hi,

In this short post, I will try to explain "friend class" in Abap.

Normally in object oriented programing, visibility of properties of a class is limited with three scopes, Public, protected and private.

 

Public is open to all from anywhere.

Protected is open only to subclasses.

Private is only for classes itself. Normally can not be reached from outside of class.

 

But, with Friend classes you can reach even private properties of an object with out inheritance. That becomes useful when you want to implement factory pattern or in unit testing(Check comments, there is a comment from Paul Hardy!). Please check examples below;

Super class ,ZBMLCL_C_SUPER ,where you define friend classes. In this example only ZBMLCL_C_SUPERS_FRIEND is defined. You can define friend classes from form interface of class too.
CLASS ZBMLCL_C_SUPER DEFINITION
PUBLIC
CREATE PUBLIC
GLOBAL FRIENDS ZBMLCL_C_SUPERS_FRIEND "That means, ZBMLCL_C_SUPERS_FRIEND can read even private properties of this class.
.

PUBLIC SECTION.

DATA: SUPER_DATA_PUBLIC TYPE CHAR1.
METHODS RETURN_VALUE_PUBLIC RETURNING VALUE(RES) TYPE CHAR1.

PROTECTED SECTION.
DATA: SUPER_DATA_PROTECTED TYPE CHAR1.
METHODS RETURN_VALUE_PROTECTED RETURNING VALUE(RES) TYPE CHAR1.

PRIVATE SECTION.
DATA: SUPER_DATA_PRIVATE TYPE CHAR1.
METHODS RETURN_VALUE_PRIVATE RETURNING VALUE(RES) TYPE CHAR1.
ENDCLASS.



CLASS ZBMLCL_C_SUPER IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZBMLCL_C_SUPER->RETURN_VALUE_PRIVATE
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RES TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD RETURN_VALUE_PRIVATE.
RES = 'S'.
ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZBMLCL_C_SUPER->RETURN_VALUE_PROTECTED
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RES TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD RETURN_VALUE_PROTECTED.
RES = 'S'.
ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_C_SUPER->RETURN_VALUE_PUBLIC
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RES TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD RETURN_VALUE_PUBLIC.
RES = 'S'.
ENDMETHOD.
ENDCLASS.

You can define friend classes in Friends tab too.


 

and consuming class ZBMLCL_C_SUPERS_FRIEND ( friend of super )

As you can see in code below, without inheritance, you can use / call protected, even private methods and properties.
CLASS ZBMLCL_C_SUPERS_FRIEND DEFINITION
PUBLIC
"INHERITING FROM ZBMLCL_C_SUPER "protected methods and protected properties will be allowed with re-definition of methods
CREATE PUBLIC.

PUBLIC SECTION.
METHODS TEST_FRIEND.

ENDCLASS.
CLASS ZBMLCL_C_SUPERS_FRIEND IMPLEMENTATION.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_C_SUPERS_FRIEND->TEST_FRIEND
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD TEST_FRIEND.
DATA(REF_SUPER) = NEW ZBMLCL_C_SUPER( ).

REF_SUPER->SUPER_DATA_PRIVATE = 'S'. "Because this class is defined as friend in super class
data(prv) = REF_SUPER->RETURN_VALUE_PRIVATE( ).

REF_SUPER->SUPER_DATA_PROTECTED = 'S'. "Because this class is defined as friend in super class
data(prc) = REF_SUPER->RETURN_VALUE_PROTECTED( ).

REF_SUPER->SUPER_DATA_PUBLIC = 'S'. "That is just public, no need for inheritance or for friend definition
data(pub) = REF_SUPER->RETURN_VALUE_PUBLIC( ).

"ME->SUPER_DATA_PROTECTED = 'S'. "Because of inheritence
ENDMETHOD.
ENDCLASS.

As you can see in this simple example, even without inheritance you can still reach properties of friend class.

 

And here some links to read in depth.

https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenfriends.htm 

https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abapclass_options.htm

 

Addition 12.10.2022 - An example of usage with factory pattern is added, in order to address  the question, that came in comments from Shai.

Below there is a factory class example. An example to understand where friendship can be used, especially with instance creation limitation feature.

Pay attention to create protected and private words in classes. Because of that limitations Mail_SERVER_BASE, LINUX and WINDOWS classes' instance can not be created publicly and needs to be created via factory class.


 

Base class, declares friendship to an empty interface. So any class that implements interface will have right to access private and protected members and also can create instance of subclasses.
CLASS ZBMLCL_CL_MAIL_SERVER_BASE DEFINITION
PUBLIC
CREATE PROTECTED "An Instance can not be created publicly!!!
GLOBAL FRIENDS ZBMLCL_IF_MAIL_SERVER_FRIEND
.

PUBLIC SECTION.

METHODS SEND_EMAIL
IMPORTING
IMPORT_DATA TYPE CHAR1
EXPORTING
EXPORT_DATA TYPE CHAR1 .

PRIVATE SECTION.

METHODS CREATE_INSTANCE
IMPORTING
SERVER_TYPE TYPE CHAR1
RETURNING VALUE(INSTANCE) TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE.

ENDCLASS.



CLASS ZBMLCL_CL_MAIL_SERVER_BASE IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZBMLCL_CL_MAIL_SERVER_BASE->CREATE_INSTANCE
* +-------------------------------------------------------------------------------------------------+
* | [--->] SERVER_TYPE TYPE CHAR1
* | [<-()] INSTANCE TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CREATE_INSTANCE.
TRY.

CONSTANTS:
WINSRV TYPE STRING VALUE `ZBMLCL_IF_MAIL_SERVER_WINDOWS`,
LINSRV TYPE STRING VALUE `ZBMLCL_IF_MAIL_SERVER_LINUX`.

IF SERVER_TYPE EQ 'W'.
CREATE OBJECT INSTANCE TYPE (WINSRV).
ELSE.
CREATE OBJECT INSTANCE TYPE (LINSRV).
ENDIF.

CATCH CX_SY_CREATE_DATA_ERROR
CX_SY_CREATE_OBJECT_ERROR.
"Some error logic
ENDTRY.
ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_CL_MAIL_SERVER_BASE->SEND_EMAIL
* +-------------------------------------------------------------------------------------------------+
* | [--->] IMPORT_DATA TYPE CHAR1
* | [<---] EXPORT_DATA TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_EMAIL.
"raise not implemented !!!
ENDMETHOD.
ENDCLASS.

 

Empty interface
interface ZBMLCL_IF_MAIL_SERVER_FRIEND
public .

endinterface.

 

SubClasses
CLASS ZBMLCL_CL_MAIL_SERVER_WINDOWS DEFINITION
PUBLIC
INHERITING FROM ZBMLCL_CL_MAIL_SERVER_BASE
CREATE PRIVATE . "Instance creation is limited!

PUBLIC SECTION.

DATA: WIN_PUBLIC_DATA TYPE CHAR1.

METHODS SEND_EMAIL
REDEFINITION .

ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SERVER_WINDOWS IMPLEMENTATION.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_CL_MAIL_SERVER_WINDOWS->SEND_EMAIL
* +-------------------------------------------------------------------------------------------------+
* | [--->] IMPORT_DATA TYPE CHAR1
* | [<---] EXPORT_DATA TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_EMAIL.
"Windows server logic to send email
EXPORT_DATA = 'S'.
ENDMETHOD.
ENDCLASS.



CLASS ZBMLCL_CL_MAIL_SERVER_LINUX DEFINITION
PUBLIC
INHERITING FROM ZBMLCL_CL_MAIL_SERVER_BASE
CREATE PRIVATE ."Instance creation is limited!

PUBLIC SECTION.

METHODS SEND_EMAIL
REDEFINITION .

ENDCLASS.
CLASS ZBMLCL_CL_MAIL_SERVER_LINUX IMPLEMENTATION.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBMLCL_CL_MAIL_SERVER_LINUX->SEND_EMAIL
* +-------------------------------------------------------------------------------------------------+
* | [--->] IMPORT_DATA TYPE CHAR1
* | [<---] EXPORT_DATA TYPE CHAR1
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_EMAIL.
"Linux server logic to send email
EXPORT_DATA = 'S'.
ENDMETHOD.
ENDCLASS.

 

Factory class that implements interface, therefore has rights to create instances.
CLASS ZBMLCL_CL_MAIL_SERVER_FACTORY DEFINITION
PUBLIC
CREATE PUBLIC .

PUBLIC SECTION.

INTERFACES ZBMLCL_IF_MAIL_SERVER_FRIEND .

CLASS-METHODS CREATE_SERVER_INSTANCE
IMPORTING
!SERVER_TYPE TYPE CHAR1
EXPORTING
SERVER TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE.

PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS ZBMLCL_CL_MAIL_SERVER_FACTORY IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZBMLCL_CL_MAIL_SERVER_FACTORY=>CREATE_SERVER_INSTANCE
* +-------------------------------------------------------------------------------------------------+
* | [--->] SERVER_TYPE TYPE CHAR1
* | [<---] SERVER TYPE REF TO ZBMLCL_CL_MAIL_SERVER_BASE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CREATE_SERVER_INSTANCE.
TRY.

CONSTANTS:
WINSRV TYPE STRING VALUE `ZBMLCL_CL_MAIL_SERVER_WINDOWS`,
LINSRV TYPE STRING VALUE `ZBMLCL_CL_MAIL_SERVER_LINUX`.

IF SERVER_TYPE EQ 'W'.
CREATE OBJECT SERVER TYPE (WINSRV).
ELSE.
CREATE OBJECT SERVER TYPE (LINSRV).
ENDIF.

CATCH CX_SY_CREATE_DATA_ERROR
CX_SY_CREATE_OBJECT_ERROR.
"Some error logic
ENDTRY.

ENDMETHOD.
ENDCLASS.

 

And finally instance consumer, mail sender class
CLASS ZBMLCL_CL_MAIL_SEND DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .

PUBLIC SECTION.

CLASS-METHODS SEND_MAIL.

PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS ZBMLCL_CL_MAIL_SEND IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZBMLCL_CL_MAIL_SEND=>SEND_MAIL
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD SEND_MAIL.

"You can not directly create instances!!! Because class creation is limited!

"Error An instance of the class "ZBMLCL_CL_MAIL_SERVER_BASE" can only be
" created within the class itself or within one of its subclasses.
"DATA(MAILSERVER) = NEW ZBMLCL_CL_MAIL_SERVER_BASE( ).

"Error An instance of the class "ZBMLCL_CL_MAIL_SERVER_WINDOWS" cannot be created outside the class. -
"DATA(WINMAILSERVER) = NEW ZBMLCL_CL_MAIL_SERVER_WINDOWS( ).

ZBMLCL_CL_MAIL_SERVER_FACTORY=>CREATE_SERVER_INSTANCE(
EXPORTING
SERVER_TYPE = 'W'
IMPORTING
SERVER = DATA(SERVER)
).

SERVER->SEND_EMAIL(
EXPORTING
IMPORT_DATA = 'Z'
IMPORTING
EXPORT_DATA = DATA(SOMEDATA)
).

"If you want to use full properties of windows type
DATA:WINSRV TYPE REF TO ZBMLCL_CL_MAIL_SERVER_WINDOWS.
WINSRV ?= SERVER.
WINSRV->WIN_PUBLIC_DATA = '1'.

ENDMETHOD.
ENDCLASS.

 

I hope that helps you.

Please feel free to add your own sample code and examples to comments.

Thanks for reading.

 
12 Comments
shais
Participant
0 Kudos
Any details about the use cases?

When/Why to use it?
TudorRiscutia
Active Participant
Nice explanation Beyhan!

Shai, to your question, a use case example can be a builder class.
beyhan_meyrali
Active Contributor
Hi Shai,

I have added an example to post. Please check.

Hopefully it is helpful for you.
beyhan_meyrali
Active Contributor
0 Kudos
Thanks Tudor.
matt
Active Contributor
Factory pattern - to ensure that only the factory can create instances of the object.
shais
Participant
0 Kudos
Thanks.

I'm aware of some good use cases.

I suggested to add some info regarding it so the readers may better understand when/what it is useful for.

beyhan.meyrali , Thanks for updating the post.
hardyp180
Active Contributor
Some purists do not like the FRIEND concept because it breaks the whole OO concept of "data hiding" that is if you define something as private you want it to be be private.

In unit testing you can have an "injector" class which is only active in development and which is a friend of the factory class. Thus the injector class can inject fake test doubles into the private attributes of the factory class, so when the production code is tested in development the fake test doubles are used instead of real classes.That sounds horribly complicated, but there is a load of information available about unit testing on the internet, not that anyone is much interested.

I also use the FRIEND concept in unit testing so private methods can be tested. Some people do not like that either as they would say the reason a private method is private is so it can be changed without breaking anything external to the class even a test.

Cheersy Cheers

Paul
beyhan_meyrali
Active Contributor
0 Kudos
Great example Paul. Thank you.
joachimrees1
Active Contributor
Thanks for the Blog, it's a nice reference!

Right at the beginning, there is one piece of info that I think comes in handy: An example on how exactly FREINDS work: which class do you put it in and in wich position do you put what statement:
CLASS ZBMLCL_C_SUPER DEFINITION
PUBLIC
CREATE PUBLIC
GLOBAL FRIENDS ZBMLCL_C_SUPERS_FRIEND "That means, ZBMLCL_C_SUPERS_FRIEND can read even private properties of this class.

-> all explained/shown in 4 Lines of code, with 1 extra comment.
Nice!

Thanks again
Joachim
joachimrees1
Active Contributor
0 Kudos
Thanks for the example and explanation on how FRIEND helps with UnitTesting / ABAPUnit, Paul!
not that anyone is much interested.

do I read a bit of frustration out of this?!  ;-|

My feeling is: adaption of unit testing and tdd is slow in the ABAP word (around me),  but it is happening!

best

Joachim
joachimrees1
Active Contributor

I noticed, that I was actually MISSING an example on how exactly to define a local friend (e.g. test class) to a global class.

SAP-Help helped me a bit: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abapclass_local_friends.htm

But only looking at the example class CL_DEMO_AMDP_MESH mentioned there made it 'click' in my head:

Image Description:
ADT showing the the "Class-relevant Local Types"-Tab of class CL_DEMO_AMDP_MESH.

The Code is:

class cl_test_selects DEFINITION DEFERRED.
CLASS CL_DEMO_AMDP_MESH DEFINITION LOCAL FRIENDS cl_test_selects.

I'll keep this as a reference!

Best
Joachim

ADT showing the the Class-relevant Local Types-Tab of class CL_DEMO_AMDP_MESH

Labels in this area