Technical Articles
Interfaces and the Strange Case of Dr Jekyll and Mr Hyde
Dear community, you may know this Gothic novella by Scottish author Robert Louis Stevenson: “The Strange Case of Dr Jekyll and Mr Hyde“. The novella is about the multifaceted nature of humans. About the good and bad in people. Most of the time, the truth lies somewhere in between. Many people are neither all good nor all bad.
The story is about Dr. Henry Jekyll, a good man. But he has a very evil side. In these moments he is Mr. Edward Hyde. Two personalities in one and the same person. That gave me the idea of building a small example with two interfaces and one class in ABAP.
First of all we need an interface for Dr. Henry Jekyll (good personality):
INTERFACE zmke_if_henry_jekyll PUBLIC.
METHODS do_kind_things.
METHODS switch_personality RETURNING VALUE(result) TYPE REF TO zmke_if_edward_hyde.
ENDINTERFACE.
Then we have an interface for Mr. Edward Hyde (bad personality):
INTERFACE zmke_if_edward_hyde PUBLIC.
METHODS do_malicious_things.
METHODS switch_personality RETURNING VALUE(result) TYPE REF TO zmke_if_henry_jekyll.
ENDINTERFACE.
Both interfaces are used by a class to represent Dr. Henry Jekyll as a person. Now we have the good and the bad personality in one class. I’ve used a Singleton design pattern to emphasize the unique character of an instance of this class.
CLASS zmke_cl_henry_jekyll DEFINITION PUBLIC FINAL CREATE PRIVATE.
PUBLIC SECTION.
INTERFACES zmke_if_henry_jekyll.
INTERFACES zmke_if_edward_hyde.
CLASS-METHODS get_instance RETURNING VALUE(result) TYPE REF TO zmke_if_henry_jekyll.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-DATA instance TYPE REF TO zmke_cl_henry_jekyll.
ENDCLASS.
CLASS zmke_cl_henry_jekyll IMPLEMENTATION.
METHOD zmke_if_henry_jekyll~do_kind_things.
ENDMETHOD.
METHOD zmke_if_edward_hyde~do_malicious_things.
ENDMETHOD.
METHOD get_instance.
IF instance IS NOT BOUND.
instance = NEW #( ).
ENDIF.
result = instance.
ENDMETHOD.
METHOD zmke_if_henry_jekyll~switch_personality.
result = instance.
ENDMETHOD.
METHOD zmke_if_edward_hyde~switch_personality.
result = instance.
ENDMETHOD.
ENDCLASS.
Finally, we have an executable example class.
In the “TEST_NO_PERSONALITY_CONTROL” method you can see an instance of the class that only allows certain good or bad options for action due to the different interface reference variables.
In the “TEST_WITH_PERSONALITY_CONTROL” method, a CAST can be used to gain overall control over both options. An option that Dr. Henry Jekyll unfortunately doesn’t have in the story 🙁
CLASS zmke_cl_interface_example DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS test_no_personality_control IMPORTING out TYPE REF TO if_oo_adt_classrun_out.
METHODS test_with_personality_control IMPORTING out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zmke_cl_interface_example IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
test_no_personality_control( out ).
test_with_personality_control( out ).
ENDMETHOD.
METHOD test_no_personality_control.
DATA henry_jekyll TYPE REF TO zmke_if_henry_jekyll.
DATA edward_hyde TYPE REF TO zmke_if_edward_hyde.
henry_jekyll = zmke_cl_henry_jekyll=>get_instance( ).
henry_jekyll->do_kind_things( ).
edward_hyde = henry_jekyll->switch_personality( ).
edward_hyde->do_malicious_things( ).
IF henry_jekyll = edward_hyde.
out->write( 'Dr. Henry Jekyll and Mr. Edward Hyde are the same person.' ).
ENDIF.
ENDMETHOD.
METHOD test_with_personality_control.
DATA henry_jekyll TYPE REF TO zmke_cl_henry_jekyll.
henry_jekyll ?= zmke_cl_henry_jekyll=>get_instance( ).
henry_jekyll->zmke_if_edward_hyde~do_malicious_things( ).
henry_jekyll->zmke_if_henry_jekyll~do_kind_things( ).
ENDMETHOD.
ENDCLASS.
All sources are here on GitHub. I hope you had some infotainment 🙂
Best regards, thanks for reading and stay healthy
Michael
Thanks for sharing, Michael! Always lovely to see your posts.
I feel like something is off about this specific case though... I think more frequently we see interfaces in the "reverse" scenario when there is one class but multiple interfaces. (Polymorphism or some such.) When one class uses multiple interfaces, I believe it wouldn't really be a case like that. It would be when one object contains other objects within. E.g. a car could have a wheel interface and a chassis interface because they are integral part of it.
In the Dr. Jekyll and Mr. Hyde scenario, even though they use the same physical body, from what I recall they do not appear at the same time and barely have anything in common. Even in your small example, every method has its own code, so these might as well be their own classes, no?
This is probably more of a philosophical than development question though. 🙂
Your feelings are most likely not deceiving you. The example with the interfaces serves the story more than a clever design. In principle, a class can be offered to the consumer from different views (interfaces). In this example the class already combines behavior and person in one. That sounds to me like a violation of the SoC principle
However, the example here also has a completely different, practical side. The story of Jekyll and Hyde is quite well known and exciting because the personalities are so intertwined. Each developer would approach an implementation in their own way, and discussions about all the different solutions are exciting and a good training to recognize advantages and disadvantages
Sounds like a good task for trainees and students...
It's really cool, but... i'd like to see a real case example because, as stated by Jelena, i find hard to imagine, at the moment, an effective use of it 🙂
As you stated, it looks like a pure theorical exercise for students.
Maybe we can find a use case together? 🙂
As a suggestion: how about a class CL_FILE that represents a file. We could have two interfaces IF_FILE_READ and IF_FILE_WRITE to give consumers of the class access to CL_FILE as it is needed in the respective context.
The example I always give is from ABAP2XLSX (if anyone has heard of that?)
Here you have an interface ZIF_EXCEL_READER which imports the details of a spreadsheet file.
One concrete class is called ZCL_EXCEL_READER_2007, another is ZCL_EXCEL_READER_XLSM which reads spreadsheets that contain macros
The idea was that if a new version of Excel came out that was different enough - say Excel 2029, you would create a new class called ZCL_EXCEL_READER_2029 which implemented the interface.
At runtime the program looks at the metadata of the spreadsheet and then chooses the most appropriate concrete class, the one best suited to reading that version of Excel. This would be done by some sort of factory, so the calling program only knows about the interface and asks the factory for the best class to handle the job at hand. The calling program thus knows nothing about which class it actually gets back, only that the class can do the job requested based on the information supplied to the factory. So when a radical new version of excel comes out you do not have to change half a hundred calling programs, just create a new class that implements the interface and adjust the factory - which could make its decision via code or by a customising table.
The other main use case for interfaces is unit testing. The production code being tested must not know it is being tested (I call this the Volkswagen rule, because in that case the code DID know it was being tested and returned bogus results on purpose).
Anyway in a proper case the production code only knows the interface of the database access class or what have you. It does not know if the concrete class is the real deal or a test double. So when running for real the program uses the actual database class, when being tested it uses a test double, but in both cases the behavior (business logic) of the code being tested is identical.
Beautiful representation. I use interfaces in real life for exactly this purpose. As little direct binding between two classes as possible. Also, the public definition part of the class is a bit more manageable.