© James E. McDonough 2017

James E. McDonough, Object-Oriented Design with ABAP, 10.1007/978-1-4842-2838-8_11

11. Singleton Design Pattern

James E. McDonough

(1)Pennington, New Jersey, USA

The first stop on our voyage through the Design Patterns galaxy takes us to the Singleton design pattern, one of the design patterns found in the GoF catalog. We will find this design pattern useful when we need to guarantee that there is only a single instance of a class available during execution.

We may have multiple instances of a class, with each instance having its own unique attribute values. However, there are some classes that are intended to have one and only one instance per program execution. Some examples are the following devices through which a user interacts with a computer:

  • Monitor

  • Keyboard

  • Mouse

Although possible, it is unlikely we will ever need more than one monitor,1 one mouse, and one keyboard per user per program. We can define classes corresponding to these devices, but we may elicit some user consternation and frustration if our program were to create multiple instances to correspond to them, eventually losing track of which instance has the current information for the user.

In these cases, we can avoid such problems by creating only one instance of the class. Although this sounds simple enough, there is a difference between intending and insuring we have only one instance.

One and Only One

The Singleton design pattern is used when we need to insure that a class has one and only one instance. It is categorized by GoF with a creational purpose and an object scope. The intent behind this design pattern is the following:

  • Ensure a class only has one instance, and provide a global point of access to it. 2

This design pattern has the simplest UML class diagram of all, shown in Figure 11-1.

A447555_1_En_11_Fig1_HTML.jpg
Figure 11-1. UML class diagram for Singleton design pattern

This is one of the few design patterns where only one class box is represented in its UML class diagram. Notice that the class is defined with both static and instance members: singletonInstance is marked as a private static attribute of type singleton, whereas instanceData is marked as a private instance attribute; also, instanceOperation is marked as a public instance method while getInstance is marked as a public static method. As shown in Figure 11-1, public static method getInstance returns a reference to the instance of singleton found in static attribute singletonInstance, creating an instance if one has not yet been created.

Many programmers notice the similarity between a singleton and a static class since they share the same trait of insuring that only a single class entity is available at execution time; however, that is about all they share. They differ in the following ways:

  • Whereas instantiation does not apply to a static class, a singleton requires instantiation.

  • Whereas a static class is composed solely of static members, a singleton is composed of a combination of instance members and static members.

  • Whereas polymorphism cannot be used with the methods of a static class, it can be used with the instance methods of a singleton, where a singleton subclass can override the methods provided by its superclass.

The question naturally arises of how to insure that a class has one and only one instance. The most common way to achieve this is to define the class with private instantiability; that is, only the class itself may create instances of the class. Although this may seem to be counterintuitive (how can instances of a class be created when only the class itself is capable of creating such instances?), it is solved easily by defining the class as a hybrid class, meaning it has both static and instance members.

Recall that static members of a class are available to external entities even when no instances of the class exist. One of those static members will be an attribute defined as a reference to an instance of the class itself, and another of those static members will be a public method that can be invoked by external entities to get an instance of the class. When invoked, this public static method will check whether or not the static attribute holding a reference to the class already points to an instance, and, if not, create an instance and then pass a reference to this instance to the caller. The first time this public static method is invoked will result in an instance of the class being created and placed into the static reference attribute. Subsequent invocations will not need to create another instance because one already exists, and this same instance will be used again.

Figure 11-2 shows a UML diagram for a mouse class as a singleton, showing how the singleton instance is made available to external entities.

A447555_1_En_11_Fig2_HTML.jpg
Figure 11-2. UML diagram for a singleton mouse class

Here we see a public static method getInstance by which external entities can get a reference to the singleton instance of the mouse class. Because it is a static method, getInstance can be invoked even when no instance of a mouse class exists. The implementation of the getInstance method checks to determine whether or not the private static attribute named singleton, which holds a reference to a mouse instance, is bound to an instance of a mouse object. If not, one is created. A reference to this object is passed back to the caller.

There are variations on this theme but all follow the same principle. We define a class to have a static constructor that creates the instance upon first access, in which case the public static method to get an instance of the class does not need to check whether or not an instance exists (it will always exist by the time it can make such a check since the static constructor will have completed its processing before the public static method starts its processing). Also, in those languages where a public attribute can be marked as publicly readable but not changeable (there are ways of doing this in Java, and ABAP provides the “read-only ” qualifier for public attributes), then access to instance members of the singleton can simply be calls through the read-only static attribute of the class, provided a static constructor is in control of creating an instance of the class.

Singleton in ABAP

An ABAP example using the get_instance technique is shown below with a singleton for a screen manager, an entity of which we would not want to have more than one, since our program most likely would use only one monitor screen to manage interaction with the user.

First, Listing 11-1 shows the definition for class screen_manager .

Listing 11-1. Definition for class screen_manager
class screen_manager definition final create private.
  public section.
    class-methods: get_instance
                     returning value(instance)
                       type ref to screen_manager
                 .
    methods      : get_screen_height
                     exporting screen_height type int4
                 , get_screen_width
                     exporting screen_width  type int4
                 , set_screen_height
                     importing screen_height type int4
                 , set_screen_width
                     importing screen_width  type int4
                 .
  private section.
    class-data   : singleton      type ref
                                    to screen_manager
                 .
    data         : screen_height  type int4
                 , screen_width   type int4
                 .
endclass.

Notice the screen_manager class has a combination of both static and instance members. Notice also the create private instantiability level qualifying the class definition statement, which also includes the qualifier final 3 to indicate the class may have no inheritors. The implementation for the methods of class screen_manager might look like the code shown in Listing 11-2.

Listing 11-2. Implementation for class screen_manager
class screen_manager implementation.
  method get_instance.
    if screen_manager=>singleton is not bound.
      create object screen_manager=>singleton.
    endif.
    instance                      = screen_manager=>singleton.
  endmethod.
  method get_screen_height.
    screen_height                 = me->screen_height.
  endmethod.
  method get_screen_width.
    screen_width                  = me->screen_width.
  endmethod.
  method set_screen_height.
    me->screen_height             = screen_height.
  endmethod.
  method set_screen_width.
    me->screen_width              = screen_width.
  endmethod.
endclass.

Listing 11-3 shows a snippet of code describing how a program would make use of this type of singleton .

Listing 11-3. Report Program Using class screen_manager
o
o
data           : report_screen_height type int4
               , report_screen_width  type int4
               , screen_manager_ref   type ref
                                        to screen_manager
               .
o
o
report_screen_height            =  30.
report_screen_width             = 120.
o
o
screen_manager_ref              = screen_manager=>get_instance( ).
call method screen_manager_ref->set_screen_height
  exporting
    screen_height               = report_screen_height.
call method screen_manager_ref->set_screen_width
  exporting
    screen_width                = report_screen_width.
o
o
call method screen_manager_ref->get_screen_height
  importing
    screen_height               = report_screen_height.
call method screen_manager_ref->get_screen_width
  importing
    screen_width                = report_screen_width.

In Listing 11-3, the programmer needs to insure that a call to method get_instance of class screen_manager, which sets a reference to the singleton into global variable screen_manager_ref, is performed before that global variable is used to access the singleton instance.

The following listings show an ABAP example using the same screen manager where the singleton reference is made available by the singleton class itself. It alleviates the need for the user of the singleton instance to provide a reference variable into which a reference to the singleton can be held, in this case one to hold the reference to the screen manager instance. This example also illustrates the use of compound references, where a reference to a member uses more than one class and/or instance selector.

Listing 11-4 shows the definition for class screen_manager, with differences from Listing 11-1 highlighted.

Listing 11-4. Class screen_manager with Differences from Listing 11-1 Highlighted
class screen_manager definition final create private.
  public section.


class screen_manager definition final create private.
  public section.
    class-data   : singleton      type ref
                                    to screen_manager
                                    read-only.
    class-methods: class_constructor
                   get_instance
                     returning value (instance)
                       type ref to screen_manager
                 .
    methods      : get_screen_height
                     exporting screen_height type int4
                 , get_screen_width
                     exporting screen_width  type int4
                 , set_screen_height
                     importing screen_height type int4
                 , set_screen_width
                     importing screen_width  type int4
                 .
  private section.
    class-data   : singleton      type ref
                                    to screen_manager
                 .
    data         : screen_height  type int4
                 , screen_width   type int4
                 .
endclass.

Notice that the static attribute singleton has been moved from the private section to the public section, and is now accompanied by the read-only qualifier. Notice also that static method get_instance has been replaced by a static constructor. Listing 11-5 shows the implementation, again with differences from Listing 11-2 highlighted.

Listing 11-5. Class screen_manager with Differences from Listing 11-2 Highlighted
class screen_manager implementation.
  method get_instance class_constructor.
    if screen_manager=>singleton is not bound.
      create object screen_manager=>singleton.
    endif.
    instance                      = screen_manager=>singleton.
  endmethod.
  method get_screen_height.
    screen_height                 = me->screen_height.
  endmethod.
  method get_screen_width.
    screen_width                  = me->screen_width.
  endmethod.
  method set_screen_height.
    me->screen_height             = screen_height.
  endmethod.
  method set_screen_width.
    me->screen_width              = screen_width.
  endmethod.
endclass.

Listing 11-6 shows how a report program would make use of the singleton defined within the screen manager class through statements using compound references, again with differences from Listing 11-3 highlighted.

Listing 11-6. Report Program Using class screen_manager with Differences from Listing 11-3 Highlighted
  o
  o
  data           : report_screen_height type int4
                 , report_screen_width  type int4
                 , screen_manager_ref   type ref
                                          to screen_manager
                 .
  o
  o
  report_screen_height            =  30.
  report_screen_width             = 120.
  o
  o
  screen_manager_ref              = screen_manager=>get_instance( ).
  call method screen_manager_ref =>singleton->set_screen_height
    exporting
      screen_height               = report_screen_height.
  call method screen_manager_ref =>singleton->set_screen_width
    exporting
      screen_width                = report_screen_width.
  o
  o
  call method screen_manager_ref =>singleton->get_screen_height
    importing
      screen_height               = report_screen_height.
  call method screen_manager_ref =>singleton->get_screen_width
    importing
      screen_width                = report_screen_width.

Let’s look more closely at what is taking place in Listing 11-6 with these compound references using both the class component selector (=>) and instance component selector (->) in a single operand. The first reference to class screen_manager is the statement invoking its instance method set_screen_height through its static attribute singleton:

call method screen_manager=>singleton->set_screen_height
  exporting
    screen_height               = report_screen_height.

Here we see the highlighted string of characters following the “call method” statement to be composed of the name of a class (screen_manager), followed by a class component selector, followed by the name of a public static attribute of the class (singleton), followed by an instance component selector, followed by the name of a public instance method (set_screen_height), all with no intervening spaces between them. This constitutes a compound reference.4 Indeed, here we are accessing an instance method of a class for which our report does not even have a reference to a corresponding instance. How is this even possible? Let’s explore this in more detail.

Although the runtime environment does not behave exactly this way, it is helpful to conceive the execution of this statement in the following way:

  1. After moving past the “call method” words starting this statement, the statement parser facilitating program execution reaches the portion of the statement operand containing “screen_manager=>”. Recognizing that the class selector => denotes a reference to a class name, the statement parser at that point requests the runtime environment to load into storage, if not already loaded, the class name that precedes the class selector, in this case screen_manager. Indeed, since this is the first reference to this class, this will be the point at which the runtime environment loads class screen_manager. At that moment, its static constructor will be invoked, which, according to the implementation for this method, will initialize static attribute singleton with a reference to an object of type screen_manager.

  2. Next, the statement parser will reach the portion of the statement operand containing “singleton->”. Recognizing that the instance selector -> denotes an instance reference, the reference field preceding the instance selector, in this case singleton, defined as a static attribute of the screen_manager class, is inspected to determine the dynamic type of the instance it holds, in this case a reference to a screen_manager instance. By this time, a corresponding object of type screen_manager already will have been instantiated into this static attribute through the completion of the static constructor method, invoked during the parsing of the previous portion of this statement operand.

  3. Next, the statement parser will reach the portion of the statement operand containing “set_screen_height”, which it will recognize as the instance method of class screen_manager to be invoked through the instance reference held in reference field singleton, and will parse the remainder of the statement to resolve the parameters to be exchanged with this instance method.

  4. The statement “call method screen_manager=>singleton->set_screen_width …” will be subject to the same type of statement parsing as its predecessor . The difference this time is that class screen_manager has already been loaded during the processing of the preceding statement, and so its static constructor will not be invoked again.

Summary

In this chapter, we learned how to define a class in such a way as to insure that only one instance of it will ever exist during execution, discovering two ways in which a singleton object makes the reference to its single instance available to external entities. We now understand the similarities and differences between a singleton object and a purely static class. We also know that a singleton is a hybrid class, having both static and instance members, and that private instantiability is one of its significant characteristics.

Singleton Exercises

Refer to Chapter 8 in the functional and technical requirements documentation (see Appendix B) for the accompanying ABAP exercise programs associated with this chapter. Take a break from reading the book at this point to reinforce what you have read by changing and executing the corresponding exercise programs. The exercise programs associated with this chapter are those in the 301 series: ZOOT301A through ZOOT301B.

Footnotes

1 Though not a need, many users now prefer a multiple-monitor configuration.

2 GoF , p. 127.

3 In ABAP, a class marked as create private will confer create none upon any subclasses, preventing them from both being instantiated and creating instances of the superclass. Accordingly, SAP recommends including the final qualifier in the definition of those classes that are assigned create private instantiability.

4 Such references, where any one of the three components could use up to the full 30-character maximum length of its respective component name, would not have been possible years ago in releases of SAP environments such as R/3 where the ABAP editor restricted the width of a single line to 72 characters. Extending this editor line width restriction, which now stands at 255 characters, made it possible to construct such long compound references without having to resort to cumbersome techniques to avoid overrunning the line end barrier.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset