This is a test message to test the length of the message box.
Login
ABAP OO Factory and Singleton
Created by Software-Heroes

ABAP OO - Factory Method, Factory and Singleton

1711

What do the factory method, the factory and the singleton in ABAP have in common? Let's find out step by step in this article.



In this article, we'll look at various patterns from object-oriented development and examine, using an example, how we can improve and what we can actually use the patterns for. We'll also point to some examples in the standard where the patterns are clearly visible.

 

Introduction

There are several implementation patterns in object-oriented development, and many have found their way into ABAP development. Some patterns, such as the iterator, are also found in ABAP, but this is rather rare, since the LOOP is a very powerful function and processing tables is quite simple.

In this article, we will look step by step at various patterns whose goal is to provide us with an instance of a class; this is also the commonality of the patterns mentioned. So let's get straight to the topic of object-oriented development in the next sections.

 

 

Static Class

In the first step, we provide a simple class with two methods that we will use in our development. Using the GET_TIMESTAMP method, we obtain the current timestamp in TIMESTAMPL format. The second method, SAVE_TIMESTAMP, calls the first method and saves the timestamp in a public attribute that is set to READ-ONLY and can therefore only be written by the class.

 

Implementation

Let's take a look at the implementation of the class. This serves as a basis and will be further optimized in the following steps.

CLASS zcl_bs_demo_oo_start DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    CLASS-DATA timestamp TYPE timestampl READ-ONLY.

    CLASS-METHODS get_timestamp
      RETURNING VALUE(result) TYPE timestampl.

    CLASS-METHODS save_timestamp.
ENDCLASS.


CLASS zcl_bs_demo_oo_start IMPLEMENTATION.
  METHOD get_timestamp.
    GET TIME STAMP FIELD result.
  ENDMETHOD.


  METHOD save_timestamp.
    timestamp = get_timestamp( ).
  ENDMETHOD.
ENDCLASS.

 

Usage

If we now want to use the class's methods, we first write the class name and then perform static access using the fat arrow (=>). Since we are using static methods (CLASS-METHODS), we cannot/must not create an instance.

zcl_bs_demo_oo_start=>save_timestamp( ).
DATA(timestamp) = zcl_bs_demo_oo_start=>timestamp.

 

Basically, such classes are used in the UTILITY area, where an instance should not be created first, but rather a reusable method is used that does not persist data in the class.

 

Evaluation

The class currently has several disadvantages that limit its use. We can only have one "instance" of the class, so we cannot use it multiple times in our coding and thus separate the data. The TIMESTAMP attribute can only be set once. Basically, such classes are used in development to provide helper methods that do not persist data.

 

Challenge

A static class can cause problems in our processing because we can never correctly predict its current state. Is the buffer full, has a process previously used the object, and are static attributes partially filled? Similar to function groups, static classes could lead to problems and side effects in careless hands.

 

 

Instance

In the next step, we'll make the class reusable so we can use it multiple times in our runs. The only change we need to make here is to remove the CLASS from the attributes and methods. Otherwise, no further adjustments are necessary.

CLASS zcl_bs_demo_oo_instance DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    DATA timestamp TYPE timestampl READ-ONLY.

    METHODS get_timestamp
      RETURNING VALUE(result) TYPE timestampl.

    METHODS save_timestamp.
ENDCLASS.


CLASS zcl_bs_demo_oo_instance IMPLEMENTATION.
  METHOD get_timestamp.
    GET TIME STAMP FIELD result.
  ENDMETHOD.


  METHOD save_timestamp.
    timestamp = get_timestamp( ).
  ENDMETHOD.
ENDCLASS.

 

Usage

This already changes the use of the class. We must now first create an instance with NEW before we can use the class. The methods and attributes are then accessed via thin arrows (->). In the example, we create a table that has the row type of our class. In the DO loop, we then create a new instance and store a timestamp locally. We then transfer the instance to our internal table, thus saving our own status.

DATA instances TYPE STANDARD TABLE OF REF TO zcl_bs_demo_oo_instance WITH EMPTY KEY.

DO 3 TIMES.
  DATA(instance) = NEW zcl_bs_demo_oo_instance( ).
  instance->save_timestamp( ).

  INSERT instance INTO TABLE instances.
ENDDO.

 

Rating

Our class now works with instances and we can reuse the individual objects. This allows us to store multiple values of a timestamp in different instances and use the class multiple times.

 

Challenge

We can only create and implement exactly one class, so we currently have no way of creating multiple variants of the class or implementation and using them in our code.

 

 

Interface

In the next step, we want to create the option of exchanging the implementation. To do this, we first create an interface, the blueprint for our class. The blueprint represents the public part of our class, so we outsource all PUBLIC components of our class to the interface.

INTERFACE zif_bs_demo_oo_interface
  PUBLIC.

  DATA timestamp TYPE timestampl READ-ONLY.

  METHODS get_timestamp
    RETURNING VALUE(result) TYPE timestampl.

  METHODS save_timestamp.
ENDINTERFACE.

 

Accordingly, we implement our class with the interface. The interface is included via INTERFACES, and the interface name appears in the method name in the method implementation.

CLASS zcl_bs_demo_oo_interface DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_bs_demo_oo_interface.

    ALIASES timestamp FOR zif_bs_demo_oo_interface~timestamp.
ENDCLASS.


CLASS zcl_bs_demo_oo_interface IMPLEMENTATION.
  METHOD zif_bs_demo_oo_interface~get_timestamp.
    GET TIME STAMP FIELD result.
  ENDMETHOD.


  METHOD zif_bs_demo_oo_interface~save_timestamp.
    timestamp = zif_bs_demo_oo_interface~get_timestamp( ).
  ENDMETHOD.
ENDCLASS.

 

The disadvantage, however, is that we now have to call the attribute or other methods using the interface name, which makes the mapping within the class very long and confusing. However, we can also use an alias within the class. This allows us to access it using the defined name.

ALIASES timestamp FOR zif_bs_demo_oo_interface~timestamp.

 

Now that we have an interface, we want to create an additional implementation. In this case, we implement the interface empty in a class. This gives us two classes that implement the same interface.

CLASS zcl_bs_demo_oo_empty DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_bs_demo_oo_interface.
ENDCLASS.


CLASS zcl_bs_demo_oo_empty IMPLEMENTATION.
  METHOD zif_bs_demo_oo_interface~get_timestamp.
  ENDMETHOD.


  METHOD zif_bs_demo_oo_interface~save_timestamp.
  ENDMETHOD.
ENDCLASS.

 

Usage

In Eclipse, we can display the "Type Hierarchy" in the interface using F4 or CTRL + T. This provides us with information about the interface's inheritance chain and shows our two classes that implement the interface.

 

If we now use the class, we would create our variable via the interface. This allows us to store/create all objects that implement the interface. In the example, we create both classes, call our Save method, and append the instances to the same table.

DATA instance  TYPE REF TO zif_bs_demo_oo_interface.
DATA instances TYPE STANDARD TABLE OF REF TO zif_bs_demo_oo_interface WITH EMPTY KEY.

instance = NEW zcl_bs_demo_oo_interface( ).
instance->save_timestamp( ).
INSERT instance INTO TABLE instances.

instance = NEW zcl_bs_demo_oo_empty( ).
instance->save_timestamp( ).
INSERT instance INTO TABLE instances.

 

Evaluation

With the interface, it is now also possible to create multiple implementations with the same blueprint. We can thus implement different behaviors and generate our desired class at runtime.

 

Challenge

There is still one challenge with this methodology, however, and that is the inline declaration. If we want to use the class, in the first variant, we must also adopt the interface when calling the method, which makes the method call correspondingly longer. In the second variant, we cast to the interface directly upon creation to obtain a variable with the interface type.

DATA(interface) = NEW zcl_bs_demo_oo_interface( ).
interface->zif_bs_demo_oo_interface~save_timestamp( ).

DATA(interface_cast) = CAST zif_bs_demo_oo_interface( NEW zcl_bs_demo_oo_interface( ) ).
interface_cast->save_timestamp( ).

 

Both methods are relatively long and generate more code when used, which could become confusing over time.

 

 

Factory Method

The Factory method provides us with a method in the class that creates an object of the class and returns it in a usable type. We set the creation of the class to PRIVATE, so that instances can only be created via our method. If our constructor is perhaps too complex, we can provide multiple factory methods for different scenarios.

CLASS zcl_bs_demo_oo_factory_method DEFINITION
  PUBLIC FINAL
  CREATE PRIVATE.

  PUBLIC SECTION.
    INTERFACES zif_bs_demo_oo_interface.

    CLASS-METHODS create
      RETURNING VALUE(result) TYPE REF TO zif_bs_demo_oo_interface.
ENDCLASS.


CLASS zcl_bs_demo_oo_factory_method IMPLEMENTATION.
  METHOD create.
    RETURN NEW zcl_bs_demo_oo_factory_method( ).
  ENDMETHOD.


  METHOD zif_bs_demo_oo_interface~get_timestamp.
    GET TIME STAMP FIELD result.
  ENDMETHOD.


  METHOD zif_bs_demo_oo_interface~save_timestamp.
    zif_bs_demo_oo_interface~timestamp = zif_bs_demo_oo_interface~get_timestamp( ).
  ENDMETHOD.
ENDCLASS.

 

The implementation has now changed: the class is set to CREATE PRIVATE, and we have a static CREATE method that returns an instance of the interface type. Direct creation of the class is no longer possible and is suppressed by the compiler.

 

Usage

What about the use of the Factory method? We have two examples for you showing how we can use it. In the first example, we create the instance using the CREATE method, call the SAVE method, and can then continue working with the instance. In the second example, we create an instance and directly call the GET_TIMESTAMP method to generate a new timestamp.

DATA(instance) = zcl_bs_demo_oo_factory_method=>create( ).
instance->save_timestamp( ).

DATA(new_timestamp) = zcl_bs_demo_oo_factory_method=>create( )->get_timestamp( ).

 

Evaluation

We now work with an instance and create objects using an additional method to return the correct type. This makes our code clean, and we don't have to create an additional variable or cast the instance. This means we are already following many best practices in this area.

 

Challenge

However, one challenge remains with this approach. Currently, our class is hard-coded in the source code, so we create a dependency on the specific implementation in ZCL_BS_DEMO_OO_FACTORY_METHOD. If we want to test our own object, we usually don't want to test this component (DOC - Dependend-On Component).

 

 

Factory

For the factory, we need to implement an additional class, the actual factory. The factory's task is to create specific instances based on the public description (interface). The class is defined as ABSTRACT so that no instances can be created from the class itself. We create an object of the type of our interface using the CREATE method.

CLASS zcl_bs_demo_oo_factory DEFINITION
  PUBLIC ABSTRACT FINAL.

  PUBLIC SECTION.
    CLASS-METHODS create
      RETURNING VALUE(result) TYPE REF TO zif_bs_demo_oo_interface.

ENDCLASS.


CLASS zcl_bs_demo_oo_factory IMPLEMENTATION.
  METHOD create.
    RETURN NEW zcl_bs_demo_oo_private( ).
  ENDMETHOD.
ENDCLASS.

 

Accordingly, we now need to adapt our class slightly. The CREATE method is now located in the factory, but in order for it to be able to create objects from our class (PRIVATE), we need to store the factory as a GLOBAL FRIEND. Our class remains PRIVATE so that objects can only be created in a controlled manner via the factory.

CLASS zcl_bs_demo_oo_private DEFINITION
  PUBLIC FINAL
  CREATE PRIVATE
  GLOBAL FRIENDS zcl_bs_demo_oo_factory.

  PUBLIC SECTION.
    INTERFACES zif_bs_demo_oo_interface.
ENDCLASS.


CLASS zcl_bs_demo_oo_private IMPLEMENTATION.
  METHOD zif_bs_demo_oo_interface~get_timestamp.
    GET TIME STAMP FIELD result.
  ENDMETHOD.


  METHOD zif_bs_demo_oo_interface~save_timestamp.
    zif_bs_demo_oo_interface~timestamp = zif_bs_demo_oo_interface~get_timestamp( ).
  ENDMETHOD.
ENDCLASS.

 

Usage

To create and use our object, we use the factory in our code; the actual class is no longer of interest to us at this point. We use the factory to create an instance with a specific implementation and use all public attributes and methods via the interface.

DATA(instance) = zcl_bs_demo_oo_factory=>create( ).
instance->save_timestamp( ).

DATA(saved_timestamp) = instance->timestamp.

 

Evaluation

The actual implementation in the ZCL_BS_DEMO_OO_PRIVATE class is now decoupled by the factory. If the factory provides us with a test implementation in the unit test, for example, via an injector into the factory, then we have created a testable environment.

 

Challenge

The biggest challenge in this scenario is managing and creating the objects, since we have to create at least three objects here. There is now also an automatic solution for the cloud that should help you with the creation. With MIA (My IDE Actions), you can create the desired objects in a package.

 

 

Singleton

In the last step, we implement a singleton. You can generally work with an interface here or directly with the class when it comes to creation and management; this covers both instance and interface scenarios. We set the singleton to PRIVATE creation so that the GET_INSTANCE method is used. Additionally, we have a static attribute that holds an instance of the class for us.

CLASS zcl_bs_demo_oo_singleton DEFINITION
  PUBLIC FINAL
  CREATE PRIVATE.

  PUBLIC SECTION.
    DATA timestamp TYPE timestampl READ-ONLY.

    CLASS-METHODS get_instance
      RETURNING VALUE(result) TYPE REF TO zcl_bs_demo_oo_singleton.

    METHODS get_timestamp
      RETURNING VALUE(result) TYPE timestampl.

    METHODS save_timestamp.

  PRIVATE SECTION.
    CLASS-DATA singleton TYPE REF TO zcl_bs_demo_oo_singleton.
ENDCLASS.


CLASS zcl_bs_demo_oo_singleton IMPLEMENTATION.
  METHOD get_instance.
    IF singleton IS NOT BOUND.
      singleton = NEW zcl_bs_demo_oo_singleton( ).
    ENDIF.

    RETURN singleton.
  ENDMETHOD.


  METHOD get_timestamp.
    GET TIME STAMP FIELD result.
  ENDMETHOD.


  METHOD save_timestamp.
    timestamp = get_timestamp( ).
  ENDMETHOD.
ENDCLASS.

 

When creating the instance via GET_INSTANCE, we first check whether a saved instance already exists and, if not, always return it to the caller.

 

Usage

In our example, we return an instance and save the timestamp. In the second step, we call the method again and save the instance in a variable. Finally, we compare the two timestamps; they should be the same, since we always receive the same instance.

DATA(save_instance) = zcl_bs_demo_oo_singleton=>get_instance( ).
save_instance->save_timestamp( ).

DATA(empty_instance) = zcl_bs_demo_oo_singleton=>get_instance( ).

IF save_instance->timestamp = empty_instance->timestamp.
ENDIF.

 

If we look at the two variables in the debugger, we can see that they point to the same object.

 

Evaluation

The singleton's main purpose is to ensure that the same instance, and thus the same data, is always received throughout the entire process. This means that an instance does not have to be passed from class to class, but can always be determined centrally throughout the entire process. However, be careful if the process or a caller expects a new object and was not expecting a singleton.

The singleton is therefore a mixture of the patterns shown, since we use an instance and very likely an interface, and provide a similar variant to the factory method via the GET_INSTANCE method. The difference, however, is that we always receive the same instance in our process.

 

Challenge

The challenge in this case is that we can only ever create objects. However, we can also invest a little more logic to manage different instances via a parameter. The management is still left to the class.

 

Standard Examples

In this section, we'll look at examples from the standard and where you can find the various patterns. Some examples will certainly seem very familiar to you because you already use them in everyday life.

  • Static - With the class CL_ABAP_CONTEXT_INFO we can obtain system information that is typically located in SYST.
  • Instance - To pack files, you can use the class CL_ABAP_GZIP_BINARY_STREAM, which does not provide an interface, but does provide an instance.
  • Interface - A basic implementation in the Application Jobs area can be found in the class CL_APJ_JT_CHECK_BASE; this is more of a fallback implementation if nothing is passed.
  • Factory Method - The factory method of the class CL_SXML_STRING_READER creates an object for reading of XML streams. However, the constructor was created in the PUBLIC area.
  • Factory - With CL_IAM_BUSINESS_USER_FACTORY you create new objects for reading and modifying business users.
  • Singleton - Finding a shared singleton in the standard is relatively difficult and also a fairly rarely used concept. With CL_SWF_TST_CONTAINER_SINGLETON you can create test containers in the classic workflow.

 

Summary

There are various approaches to implementing implementations. In most cases, each method builds on a predecessor and uses the concepts to provide an improved variant or slightly modify its functionality. If you want to build on a secure infrastructure that is also flexibly interchangeable and testable, you should go with the factory, as it adopts most best practices and remains open.

 

 

The singleton gives you the ability to access the same instance, and thus the same data, at different points in your development or process. This variant has advantages because you don't have to loop the instance through the process, and sometimes you don't even have the option to do so when it has to go from BADI implementation to BADI implementation.

Generally, you should avoid static classes, especially when it comes to data handling and storage within attributes. Static attributes and methods, when used correctly, can generate added value, but only sparingly and selectively. If you are unsure or don't know exactly what to do in your case, then take a look at the Clean ABAP Style Guide.

 

Complete example

You can find the complete source code of the examples in our corresponding GitHub repository. You'll find most of the coding in this article, but we've only shown the most important parts, especially when it comes to implementation and usage.

 

Conclusion

Congratulations if you made it to the end of the article. Keeping the different patterns and methods side by side makes a lot of sense, as they partly build on each other and evolve. Each pattern can be used in different scenarios to add value to development.

 

More information:
SAP Help - Static Classes and Singletons
SAP Help - ABAP Objects


Included topics:
OOABAP OOFactorySingleton
Comments (4)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ADT - MIA Select Converter

Category - ABAP

For MIA, there is now a new action in the ABAP Development Tools that allows you to convert your old Open SQL statements to ABAP SQL and ABAP Cloud.

03/18/2025

ADT - My IDE Actions

Category - ABAP

Write your own functions in the ABAP Development Tools? No problem anymore with the standard IDE Actions and also without Java.

02/18/2025

ABAP Tools - Working with Eclipse (Storage)

Category - ABAP

How do you access deleted source code in the ABAP Development Tools even though you never transported it? More about how ADT uses the storage and how it works.

10/29/2024

ABAP Tools - Working with Eclipse (Multiple Debugging Sessions)

Category - ABAP

How are multiple debugging sessions managed in the ABAP Development Tools in Eclipse? More information here.

10/08/2024

ABAP Tools - Working with Eclipse (SAP GUI Language)

Category - ABAP

Do you have the wrong SAP GUI language when you start the GUI in the ABAP Development Tools? You can find the solution here.

09/10/2024