© James E. McDonough 2017

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

3. Encapsulation

James E. McDonough

(1)Pennington, New Jersey, USA

We start our journey to Objectropolis from the perspective of Encapsulation, largely because this is the object-oriented design concept with which most procedural programmers already are familiar. The word encapsulate means “to encase in or as if in a capsule.”1 Procedural programmers have been doing this for years via subroutines and locally-defined variables.

To illustrate this concept, let’s establish a familiar frame of reference for encapsulation. Suppose we write a procedural program like any one of thousands you might find in your own programming environment. It might be constructed similar to the outline shown in Listing 3-1.

Listing 3-1. Representative Procedural Program
program bnx0037
global_variable_1
global_variable_2
global_variable_3
   o
   o
   o
global_variable_n
main_routine
  do subroutine_a
  do subroutine_b
subroutine_a
  local_variable_1
  local_variable_2
  statement 1
  statement 2
subroutine_b
  local_variable_3
  statement 3
  statement 4

With this example we see a program containing a set of global variables, a main driving routine, and two subroutines. The two subroutines illustrate encapsulation of code, procedures to be performed from some other point in the program, in this case from the main routine. These subroutines also illustrate encapsulation of data fields, where each subroutine has its own local variables. For this program, the subroutines are visible and available only within the program unit bnx0037 and are not visible beyond the constraints of this program unit2.

The global variables also are visible and available from anywhere within he program unit bnx0037, but are not accessible beyond this program unit. Similarly, access to local variables defined within a subroutine are visible and available from anywhere with the subroutine3, but are restricted to the scope of the subroutine, and are not visible beyond the constraints of the subroutine in which the local variable is defined.

Separation of Concerns

Encapsulation is a technique used to facilitate modularizing code in pursuit of the design principle known as separation of concerns. This concept, credited as first being presented by Edsger W. Dijkstra in a 1974 paper he wrote titled “On the Role of Scientific Thought”4, addresses the maintenance benefits to be gained by arranging software components into modules such that the processing performed by a single module is limited to a specific concern and does not cross the distinct boundaries separating areas of processing. This often is illustrated using the Model-View-Controller design pattern, which segregates the programming logic for the application (model), presentation (view), and manipulation (controller) of information into separate components.

Visibility

Encapsulation is often associated with the concept of information hiding 5 , whereby elements and implementations defined in software components can be segregated from each other and from outside entities under the expectation that subsequent changes would not require a proliferation of modifications across many components. The degree to which information within a class can be hidden is facilitated in many object-oriented languages through the assignment of a visibility level to that information.

The term visibility describes a fundamental concept associated with the object-oriented principle of encapsulation. Each member of each class has a visibility level, controlled by the programmer, similar to the global and local visibility just described in the procedural program example. The visibility of a member is a designation of how that member is visible to other entities. With object-oriented programming, visibility is described using neither global nor local, but using other words that denote gradations between most visible and least visible.

To illustrate this, we’ll use the tapered shape diagram shown in Figure 3-1, which illustrates maximum visibility at the widest part and minimum visibility at the narrowest part, and places the visibility descriptors in their natural sequence from least visibility to most visibility.

A447555_1_En_3_Fig1_HTML.jpg
Figure 3-1. Visibility gradations

The bottom of the taper depicts the most restrictive visibility to class members. This lowest level of visibility is reserved for method variables, which are the functional equivalent of local variables in procedural programming.6 Technically, these method variables are not themselves members of classes, but are variables defined within methods that are members of classes.

The next widest level of visibility is called private. It describes the visibility level of members whose visibility is restricted to this class only, and might be considered the functional equivalent to the visibility of both subroutines and global variables in procedural programming.

The next widest level is called package 7. It describes the visibility level of members whose visibility is restricted to this class and any other components within the same package. This visibility level has no equivalent in procedural programming. In Java, this is known as package-private visibility.

The next widest level is called protected. It describes the visibility level of members whose visibility is restricted to this class, any other components within the same package and any classes inheriting8 from this class. This visibility level has also no equivalent in procedural programming.

The next widest level is called public. It describes the visibility level of members whose visibility is not restricted at all; that is, a member with public visibility is visible to any other component within the programming environment. This visibility level is similar in nature to the way ABAP reports are available to any other component through the SUBMIT statement, as well as to the way ABAP function modules are available to any other component through the CALL FUNCTION statement, to the way global class and interface definitions in ABAP are available to any other component simply by referring to the global class or interface, and to the way domains, data elements, tables, etc. defined in the ABAP DataDictionary are available to virtually any other objects defined within the ABAP repository.

Visibility levels also are known as access modifiers. Table 3-1 summarizes the visibility settings for class members.

Table 3-1. Visibility Levels

Visibility level

Accessibility

Public

Accessible to any other entity within the same environment.

Protected

Accessible within this class, within any other entities assigned the same package, and within any inheritors of this class.

Package

Accessible within this class and within any other entities assigned the same package.

Private

Accessible within this class only.

Method variable

A variable is accessible only within the method in which it is defined.

Figure 3-2 shows the same information using a different format9, where the rows of the visibility level column show increasingly wider levels of visibility from bottom to top, and where the columns to the right of the visibility level column show increasingly greater levels of access from right to left. Notice how the “Yes” and “No” accessibility values fall evenly distributed on both sides of the diagonal line.

A447555_1_En_3_Fig2_HTML.jpg
Figure 3-2. Visibility levels with indications of access to same class, same package , subclass, and other entities

Let’s explore this concept through an example. Suppose we have groves of oak trees in various settings around town, offering the benefits of shade in summer and spectacular changes of color in autumn. We will define a visibility level to each grove of oak trees based upon the particular setting in which we find it.

First we will consider a grove of oak trees we find in the public park in the center of town. This grove of oak trees is a member of the park, and being a public park, the grove is accessible to all residents of the town as well as to all non-residents of the town. The benefits offered by this grove are available to everyone who strolls through the park. Accordingly, we assign the grove of oak trees public visibility.

Next we will consider a grove of oak trees we find in a wildlife sanctuary located on the outskirts of town. This grove of oak trees is a member of the wildlife sanctuary, and being a protected place, the grove is not accessible to all town residents and non-residents. While there are many ways these folks can become associated with the wildlife sanctuary, the benefits offered by this grove of oak trees are available only to those who have a reason for strolling through this protected wilderness. Accordingly, we assign the grove of oak trees protected visibility , meaning only those folks with a specific type of association to the wildlife sanctuary, and their friends,10 have access to the grove of oak trees.

Next we will consider a grove of oak trees we find in a common area of a gated community also located on the outskirts of town. This grove of oak trees is a member of the common area of the gated community. The maintenance for this grove of oak trees comes from the homeowners association fees paid by the residents who live in the gated community. The homeowners association fees are part of the package of regulations accepted by all residents of the gated community for the privilege of living within its walls. The grove is accessible only to the residents of the gated community. Accordingly, we assign the grove of oak trees package visibility , meaning only those folks who have accepted the same package of regulations for being residents of the gated community and their friends have access to the grove of oak trees.

Next we will consider a grove of oak trees we find in the backyard of a house located near the center of town. This grove of oak trees is a member of the property on which the house sits, and since it is located on private property, the grove is accessible only to the residents of this house. Accordingly, we assign the grove of oak trees private visibility , meaning only the residents of the house on this property and their friends have access to the grove of oak trees.

Finally we will consider a grove of oak trees appearing in a picture hanging on the wall of the den we find in that same house near the center of town. This grove of oak trees is a member of only one room of the house. Residents of the house have access to this grove of oak trees only when they enter the den, and it immediately becomes unavailable to them when they leave the den. Accordingly, we assign the grove of oak trees method variable visibility , meaning only those residents engaged in the behavior of using the den have access to the grove of oak trees.

Regarding visibility, the conventional wisdom within the object-oriented community is to define these variables, attributes, and methods with the most restrictive visibility possible while still enabling access to them as necessary. This means

  • Variables should be defined as local method variables unless there is a compelling reason for them to be defined as attributes of the class.

  • Class members should be assigned private visibility unless there is a compelling reason for the visibility to be set to a wider visibility level.

My advice with new object-oriented development is to define all variables as method variables and all methods with private visibility , and then to widen the visibility level as necessary, but then only to the extent of the least visibility that will still accommodate the required access.

Why Visibility Matters

My experience with existing procedural programs has been to find a proliferation of global variables that have no reason to be defined with global visibility. This occurs for a variety of reasons:

  • The author developed a bad habit of defining all variables with global visibility rather than giving any thought towards which of them should be defined locally.

  • The author recognized there were multiple subroutines that required using the same set of local variables, and so defined them once globally rather than locally for each subroutine.

  • The author might have been enticed by the presence of program segments, generated manually or automatically, the primary purpose of which is to contain global components of programs.11

  • Some aspects of the programming language or environment work only when interacting with variables defined globally.12

  • A precedent had been set by the original author, who defined variables unnecessarily with a global visibility, and this style was perpetuated by subsequent maintenance programmers to retain stylistic consistency.

Regardless of the reason why, the indiscriminate use of global variables is one of the biggest impediments to subsequent maintenance efforts. Too often it requires the maintenance programmer to perform an exhaustive search through the program for each global variable to determine whether or not it is safe to use or change its value at some critical point in the code. Frequently it is expediency in writing the first release of a program that lures developers into taking shortcuts with the design, trading ease of initial design for subsequent ease of maintenance. Since it is likely that much more time will be spent in the maintenance of a program than in its initial design, such cavalier inattention to variable visibility eventually causes problems, and usually it is only during subsequent maintenance efforts where the consequences become evident.

Visibility assigned at the most restrictive level to permit the necessary processing becomes a benefit during maintenance efforts by reducing the time it takes the developer to identify the necessary changes required as well as enabling the refactoring of code to be performed without any concern for rendering other components syntactically invalid. This is why visibility matters. Accordingly, developers who take the time to apply the appropriate visibility levels to program variables during design enable subsequent maintenance programmers to reap the benefit of shorter maintenance efforts.

With most of my object-oriented endeavors, I have found that attributes usually have a more restrictive visibility assigned to them than behaviors. Indeed, I usually tell my students that if they follow the advice offered by object-oriented scholars they will probably arrive at a class design that incorporates “public behaviors and private attributes.” This arrangement stems from the necessity to offer external entities access to the values of the attributes of a class but not to the attributes themselves. Accordingly, many times there will be definitions for methods known as getter and setter methods, behaviors that offer public access by external entities to values of attributes of a class that are defined with a visibility level other than public . External entities use getter methods to make a request to get the value of an attribute of the class, and setter methods to make a request to set the value of an attribute of the class. In both cases, the class is in complete control over whether the value of the attribute will be allowed to be retrieved by the getter method or altered by the setter method.

Realm

In some object-oriented environments, members of classes belong to one of two different realms:

  • Those members that are associated with a specific instance of a class

  • Those members that are not associated with a specific instance of a class

Virtually every object-oriented environment supports the definition within a class of members that are associated with a specific instance of the class. Those that also offer support for class members not associated with a specific instance of the class include C++, C#, Visual Basic, Smalltalk, Java, and, of course, ABAP. In most of these languages, those members not associated with a specific instance of a class are known as static members.13 Accordingly, we can refer to which one of these two realms a member is associated by the following terms:

  • Instance member

  • Static member

In many object-oriented languages, including C++, C#, Java, and ABAP, a static member is marked so by including a qualifier with the definition of the member indicating it is a static member, with the absence of a such a qualifier indicating it is an instance member. Indeed, in the languages C++, C#, and Java, the qualifier used with a member definition to denote that it is a static member is the word static. 14

Whereas instance members are bound with a specific instance of the class, static members are available and accessible to all instances of the same class. Indeed, static members of a class are available during execution even when there have been no instantiations of the class, meaning that these members are available immediately once a program containing the class definition begins to execute. It is not necessary to create any instances of the class to use its static members. The same rules of visibility apply equally to static members and instance members.

An instance attribute will exist once for each instance of the class with which it is associated. In contrast, a static attribute of a class exists only once for the entire execution of the program and is shared across all instances of its class. Instance methods have access to both the instance members and static members of its class. In contrast, static methods have access only to the static members of its class. Instance members exist only upon instantiation of the class, and only for as long as the instance remains active. In contrast, static members exist during the entire execution of the program, irrespective of the presence or absence of instances of its class.

The following example expands upon our fox and dog classes from the preceding chapter. Here we have included some new members for the dog class, shown highlighted in Table 3-2:

  • A new instance attribute called registration_number

  • A new instance behavior called set_registration_number, which will use the value it receives to set the new instance attribute registration_number

  • A new static attribute called last_used_registration_number

  • A new static behavior called get_next_registration_number, which will add 1 to the last_used_registration_number and return the new value

Table 3-2. Attributes and Behaviors for the Fox and Dog Classes

Class

Fox

Dog

Attributes

alacrity

color

alacrity

registration_number

static last_used_registration_number

Behaviors

jump

set_registration_number

static get_next_registration_number

Notice that all members of the fox and dog classes that are not explicitly described as static are, by default, instance members. This includes all of the attributes and behaviors of the fox, the alacrity and registration_number attributes of the dog, and the set_registration_number behavior of the dog. The dog class also has a static attribute called last_used_registration_number and a static behavior called get_next_registration_number. Notice also that the dog class now has a combination of both instance and static members.

Now, each time we create a new instance of class dog, we can assign it a registration number unique from all other dog instances simply by invoking the static method get_next_registration_number of the dog class to get the next value and then calling the instance method set_registration_number to apply it to our dog instance attribute registration_number. This works because the static attribute last_used_registration_number is available to all dog instances. Upon creating the first dog instance and invoking the static behavior get_next_registration_number, the static attribute last_used_registration_number would be incremented and the new value returned to us, which we would place into the instance attribute registration_number of our new dog instance via the set_registration_number behavior. Then when we create a second dog instance, we would go through the same series of actions but we would get a different registration number. This is because the static members are shared amongst all instances of the dog class, and retrieving a registration number for our second dog instance uses the same static attribute that had been used to register our first dog instance. Accordingly, if we were to continue using this sequence we would get a unique registration number with each new instantiation of a dog class, as each new dog instance leaves behind its registration number in the static attribute last_used_registration_number for the next dog instance to see.

This concept of instance members versus static members may be difficult to grasp, so let’s use a metaphor that illustrates it more clearly. Suppose we learn that our management has made arrangements for us to attend one of the annual technology conventions in Las Vegas, Nevada this year. Upon arriving at our hotel we find that we have a reservation for one of the hotel guest rooms, each of which is virtually identical to each of the other guest rooms. Accordingly, our reservation permits us to occupy one instance of a guest room. Others who are attending the same technology convention also are staying at the same hotel, but each person has a unique hotel reservation, permitting each person to occupy some other instance of a guest room. Our room has a unique room number on the door and we have been issued a card key for entrance to the room.

This is the only instance of hotel room to which we have sole access; it is our specific instance. Upon entering the room, we can turn on one of its many lights, tune the television to our favorite channel, and adjust the climate controls of our guest room without these changes having any effect upon the lights, television, or climate of any other guest room in the same hotel. Indeed, the other guests could be making similar changes to lights, television, and climate in their guest rooms, but their changes do not affect our guest room. We could sit in one of the chairs in our guest room, and then even proceed to rearrange the furniture in our room, with no effect upon the furniture in any of the other guest rooms. In this case, the lights, television, climate controls, and furniture constitute instance attributes of the hotel room, accessible only to the guest occupying the room, and the actions turn_on_guest_room_light, tune_guest_room_television, adjust_guest_room_climate_controls, and rearrange_guest_room_furniture constitute instance methods applicable to these instance attributes.

Later, we decide to leave our guest room and visit the hotel lobby, a room in the hotel shared by all of the hotel guests. The lobby also has lights, a television, climate controls, and furniture, things that are not associated with a specific hotel guest but are available to all hotel guests, and constitute static members of hotel room. Now if we find an empty chair in the lobby and sit in it, the chair cannot be used by any of the other hotel guests until we unseat ourselves from it. Similarly, if we were to reset the lobby lights, change the channel on the lobby television, alter the lobby climate controls, and rearrange the lobby furniture, these changes would immediately affect every hotel guest who also is visiting the lobby. In addition, when one of the other hotel guests makes changes to these things while we are in the lobby, these changes immediately affect us. In this case, the lights, television, climate controls, and furniture constitute static members of hotel room, shared by and accessible to all hotel guests, and the actions turn_on_lobby_light, tune_lobby_television, adjust_lobby_climate_controls, and rearrange_lobby_furniture constitute static methods applicable to these static attributes. As guests of the hotel, we have access to the instance members of our own guest room as well as those static members located in the hotel lobby.

Furthermore, we could depart our own hotel and walk down the street to the next hotel, where none of its guests have yet arrived and registered. Accordingly, it has no instances of guest room occupants. Despite no instances of guests, and even though we have no intention of registering as a guest at this hotel, we still are able to walk into its lobby and sit in a seat, which now cannot be used by anyone else visiting the lobby of this hotel. Indeed, there is nothing to stop us from resetting the lobby lights, changing the channel on the lobby television, altering the lobby climate controls, and rearranging the lobby furniture,15 all of which would have an immediate effect upon every other person also visiting the lobby. In this case, the lights, television, climate controls, and furniture still constitute static members of this hotel room, but now we see that even though they are shared by and accessible to all hotel guests, they also are available to those who are not guests of the hotel, even at a time when the hotel does not yet have any guest room occupants. Because we are not guests of this hotel, we have access only to the static members of the hotel room, and not to any instance members of a guest hotel room.

In summary, an instance attribute exists once per instance of the class, and each one is unique and separate from the instance attributes of other instantiations of the class, whereas static attributes exist once per program execution, are available to all instances of the class, and are available to external entities even when there are no corresponding instances of the class. Instance methods have access to both the instance attributes and static attributes of its class, whereas static methods have access only to the static attributes of its class.

The Encapsulation Unit

Encapsulation unit is a term referring to the boundaries of encapsulation exhibited by an entity. In object-oriented design, encapsulation units exist at two different levels.

At one level, the entire class definition serves as the encapsulation unit. It can be regarded as a container with a lid, as shown in Figure 3-3. The walls of the container define the boundaries of the encapsulation unit. External access to the class members is available only through the public interface, which is represented by the neck of the container and its open lid. Only those attributes and behaviors that have public visibility are accessible through the public interface. Class members having any other visibility are not publicly accessible, but they are contained within the class encapsulation unit and are accessible to the other members within the same class.

A447555_1_En_3_Fig3_HTML.jpg
Figure 3-3. Encapsulation unit

A new class-level encapsulation unit is established when a class is instantiated. This encapsulation unit remains in effect for the life of the class instance.

At another level, each method of the class is itself an encapsulation unit, allowing for the definition of local variables that are not available outside the method. A new method-level encapsulation unit is established with each invocation of a method, and is destroyed upon exiting the method. Any local variables defined within the method are set with their initial or default values upon entering the method.16 This means that the values of these local variables from a previous invocation are not retained for any subsequent invocations.17

Instance Encapsulation Units

We can define a class such that every one of its members belongs to the instance realm ; that is, none of its members are marked as static. This constitutes an instance class. It is necessary to create instances of an instance class in order to access its members. Instance classes are used in those cases where we need multiple instances of the class to facilitate the necessary processing, when each instance would contain the same set of attributes but when each instance has different values assigned to those attributes. For example, we might define a class sales order item for which we might expect there to be multiple instances of its objects to handle the processing for a single sales order, and we would expect that no two instances of sales order item would have identical attribute values.18

Static Encapsulation Units

We can define a class such that every one of its members belongs to the static realm ; that is, each member is marked as static. This constitutes a static class. It is not necessary to create any instances of a static class in order to access its members. Indeed, while it may be technically possible to create an instance of a static class, it would have no instance members that could be accessed. On the other hand, because a static class is not instantiated, there can be only one copy of its members available for access. This means that a static class should be used only in those cases where we need only one entity of the class to facilitate the necessary processing. For instance, we would expect to have multiple instances of the sales order item class, noted above, to handle the processing for a single sales order. Meanwhile, we might define a class called screen manager for which we certainly would not want multiple instances; a single screen manager is all we would need to manage the presentation of information to the user, and having more than one screen manager would present a conflict.

Some object-oriented scholars dismiss the idea of static classes as a technique that falls short of the necessary requirements to be truly considered object-oriented, discouraging their use and pointing out that there is no concept of object associated with a static class19. Indeed, in object-oriented environments, supporting multi-threading static classes is not considered “thread safe.”20 While it is true that static classes will not be able to take full advantage of all the principles of object-oriented programming (for instance, polymorphism is not possible within static classes), they do offer a stepping stone for procedural programmers to become more comfortable with programming in object-oriented environments. Of note, in ABAP environments, static classes are very similar to simple function groups.21 Since function groups are an area of ABAP familiar to most procedural programmers, perhaps it would be helpful to compare the two to see how they are similar and how they differ:

  • Both static classes and function groups encapsulate their components.

  • Neither static classes nor function groups need to be instantiated to be used.

  • The function modules of a function group are equivalent to the public methods of a static class.

  • The data definitions usually defined in the TOP include of a function group are equivalent to the private attributes defined for a static class.

  • The subroutines defined within function groups are equivalent to the private behaviors defined for a static class.

Static classes offer some other advantages not available to function groups:

  • A static class can define public attributes, such as constants, field formats, and structures to be used with parameters of public method signatures

    For example, a class alarm_clock can define a public type alarm_setting, defined as type single character, along with public constants alarm_on and alarm_off as type alarm_clock.alarm_setting, having values X and space, respectively, and public method set_alarm whose signature contains parameter alarm defined as type alarm_clock.alarm_setting. An external entity using class alarm_clock can now define its own field using the public type alarm_clock.alarm_setting, and then move the public constant alarm_clock.alarm_on into this field before using it with the alarm parameter on a call to the set_alarm method. In this way, the caller can refer to the class in terms the class provides.

  • The ABAP compiler enforces a stricter compliance with syntax for static classes than it can for function groups

Accordingly, a static class can be defined as a substitute for a simple function group, offering all the same features and capabilities available to simple function groups, and more.

Hybrid Encapsulation Units

We can define a class such that some of its members belong to the instance realm while other members belong to the static realm . This constitutes a hybrid class. It is not necessary to create any instances of a hybrid class in order to access its static members; however, its instance members become accessible only upon creating an instance of a hybrid class. This is a common scenario in object-oriented design.

A good example of how this can be used effectively is to take the example of the sales order item class we have been using so far and embellish it. To turn this otherwise instance class into a hybrid class, we need to include some static members. We could include a static attribute to represent the next available sales order item number as well as a static behavior to get the next sales order item number, which when invoked will increment the static attribute holding the next available sales order item number and then send this value back to the caller . Since the new attribute and new method are both static, they both are available to all instances of sales order item, with the value of the attribute holding the next sales order item number being shared across all instances of sales order item. Upon creating each new instance of a sales order item, this static method can be invoked to insure that the item number assigned to the new instance is unique amongst the set of all instances of sales order items being created.

Constructors and Destructors

A special type of method, known as a constructor, can be defined for a class to specify the activities to be undertaken during the creation of the class encapsulation unit, such as initializing the values of attributes. Another special type of method, known as a destructor, also can be defined for a class to specify the activities to be undertaken during the destruction of the class encapsulation unit, such as releasing those resources an object might have acquired.

A class encapsulation unit can be created regardless of whether or not the class has a constructor method defined for it, and similarly can be destroyed regardless of whether or not there is a destructor method defined for it. These constructor and destructor methods are defined by the programmer to instruct the runtime environment what to do in addition to creating and destroying the class encapsulation unit.

Although they are known as methods, constructors and destructors are not regarded as members of the class since they can be used only to create and destroy the class encapsulation unit and are not otherwise available during the lifetime of the class. Furthermore, constructors and destructors are defined such that each one applies either to the static realm or to the instance realm .

Instance Constructors and Destructors

Instance constructors are invoked automatically by the runtime environment when a new object is being created. The instance constructor facilitates setting the new object to an initial state , and will be executed once and only once for an object. Because new instances of objects are requested explicitly, an instance constructor can include a method signature, the parameters of which are available for use during the process of creating the instance. Instance constructors have access to both static and instance members of the class.

Instance destructors are invoked automatically by the runtime environment when an existing object is to be destroyed. The instance destructor facilitates any cleanup activities required during object destruction.

Whereas virtually every object-oriented environment supports instance constructors, only some provide support for instance destructors. Object-oriented environments providing support for the concept of Resource Acquisition Is Initialization (RAII),22 such as C++, necessarily provide support for instance destructors.23 In contrast, object-oriented environments providing support for what is known as automatic garbage collection24 employ the dispose pattern25 for resource cleanup, and some of these environments, such as Java and ABAP, provide no support for the explicit definition of instance destructors.26

Static Constructors and Destructors

Static constructors are invoked automatically by the runtime environment when a class is first accessed, which may be due to an external reference to a static member of the class or to the creation of the first instance of the class. The static constructor facilitates setting the static attributes of the class to an initial state, and will execute once and only once for the class. When the static constructor is triggered due to the creation of the first instance of the class,27 it will run to completion before creation of the first instance begins. This means that the static constructor for a class will always start and finish prior to the start of the instance constructor when creating the first instance of that class. Because there is no specific statement that will cause a static constructor to be invoked, a static constructor cannot include a method signature. Static constructors have access to the static members of the class, but cannot access instance members.

Static destructors are invoked automatically by the runtime environment when the static members of a class are to be destroyed. The static destructor facilitates any cleanup activities required during class destruction.

Only some object-oriented environments provide support for static constructors. C# and ABAP are two that do. Neither C++ nor Java supports this concept, although Java does support what are known as static initialization blocks.

Meanwhile, it is rare to find any object-oriented environment supporting static destructors. C++, C#, Java, and ABAP do not offer support for this.28

Friendship

Some object-oriented languages, among them C++ and ABAP, offer the capability for a class to enable other specific classes to have unrestricted access to its members. This effectively renders all members of the class publicly visible to those other classes. This capability is known as friendship. 29 A class offers friendship to other classes by specifying the names of those classes that it considers its friends.

Friendship only can be offered; it is not reciprocated. That is, a class offering friendship to another class does not itself suddenly become a friend of that other class; the other class also needs to offer its own explicit friendship.

Since a friend class has what amounts to public visibility to all of the members of the class offering the friendship, it has the undesirable effect of breaking encapsulation, because the class no longer is in complete control over its own members. Other classes can change the values of non-public attributes of the class offering the friendship as well as invoke its non-public methods.

Also, with friendship in effect, it is now more difficult to perform subsequent maintenance and refactoring on the class offering the friendship because now it requires more careful consideration when making changes to protected and private members. For instance, we may change the definition of a private attribute and change all of the references to it within the class and think we have completed the task, but now we may have rendered a friend class syntactically incorrect for those statements that access the private attribute in statements that relied on a specific type of definition. This also applies to the signature of private methods, which also could render friend classes invoking those methods syntactically incorrect for a variety of reasons, such as the wrong type used with a parameter, or is now missing a parameter that has not been marked optional.

Some in the object-oriented community repudiate the use of friendship while others advise using friendship with caution.

Considerations for Using Encapsulation Effectively

So, how do we go about deciding what to include in an encapsulation unit? Here are some guidelines:

  • Encapsulate repetition.

    This is a technique also used in procedural programming: write the code once and call it from multiple locations as necessary. This can be applied at the method encapsulation unit level, where a method of a class is invoked by other methods of the same class. It also can be applied at the class encapsulation unit level, where one class will provide the repetitive activities to be performed at the request of other classes.

  • Encapsulate complexity.

    This is another technique used in procedural programming: write particularly long and complex algorithms or processing sequences in their own subroutines.30 As with repetition, this also can be applied at the method encapsulation unit level, where a method of a class is invoked by another method of the same class, as well as at the class encapsulation unit level, where one class will provide the complex processing to be made available to other classes.

  • Encapsulate what is likely to vary.

    When it is known the code contains processes likely to change in the future, separate these processes into their own encapsulation units. By vary we do not mean applying changes to code for the purpose of fixing bugs, but changes to code because the user is asking for new features and capabilities. For instance, a pizza parlor might have software to handle online customer orders. The boilerplate code handling such things as estimating time to deliver the order, collecting payment, and printing receipts probably is unlikely to change often, but the various menu entries, toppings available, item prices, and promotional campaigns are likely to change to keep up with customer demands.

  • Encapsulation units should have only a single responsibility.

    Restricting encapsulation units to only a single responsibility is to comply with what is known as the Single Responsibility Principle , 31 a term coined by Robert C. Martin , who regards a responsibility as a reason to make a modification. This can be applied at the method encapsulation unit level, where, for instance, a class handling printed output will have one method to facilitate setting print parameters and another method to facilitate issuing the request for printing. If a single method were to facilitate both of these requirements, then a change to the way the method accommodates print parameters and a change to the way a request is made for printing would constitute two different reasons for changing the same method. This can also be applied at the class encapsulation unit level, where, for instance, there is one class to facilitate printing content and another class to facilitate displaying content, despite that both classes handle output of content .

ABAP Language Support for Encapsulation

The object-oriented extensions to the ABAP language accommodate encapsulation in the following ways:

  • By supporting the concept of a class with attributes and behaviors (methods)

  • By providing a way to assign a visibility level to class members

  • By facilitating the definition and use of method variables

  • By supporting both instance and static members for classes

  • By supporting instance classes, static classes, and hybrid classes

  • By supporting both instance and static constructors for classes

  • By enabling a class to offer friendship to other classes

Unlike most other object-oriented languages, the syntax for defining a class in ABAP separates its definition from its implementation. The definition component must precede the implementation component. This is achieved through these complementary class constructs:

class class_name definition [options].
  o
  o
  o
endclass.
class class_name implementation.
  o
  o
  o
endclass.

Attributes and method signatures are specified within visibility sections appearing in the definition component. Each visibility section begins with the section name and ends with the definition of the next visibility section name or with the endclass scope terminator. Those members defined within a visibility section are assigned that corresponding visibility.

Listing 3-2 shows an example of the ABAP syntax for a class describing a car.

Listing 3-2. ABAP Code for the Car Class
class car definition.
  public section.
    methods      : get_year exporting year type string
                 , set_year importing year type string
                 .
  private section.
    data         : model_year     type string
                 .
endclass.
class car implementation.
  method get_year.
    year                          = model_year.
  endmethod.
  method set_year.
    model_year                    = year.
  endmethod.
endclass.

Here we see both a public and a private visibility section in the definition component. The public section defines two methods and their signatures: get_year and set_year. The private section defines one attribute: model_year. The implementation component contains the implementation for each of the two public methods defined in the definition component.

In this example, the only statements with which an experienced procedural ABAP programmer would be expected to be familiar are the data statement defining the attribute model_year and the assignment statements in the implementations for each of the methods. Everything else is new with object-oriented ABAP.

New, perhaps, but not entirely unfamiliar. The method-endmethod construct appearing in the implementation component is functionally identical to the subroutines defined in procedural ABAP using the form-endform construct. They differ only in syntax; the procedural form-endform construct accommodates a signature on the form statement itself, whereas the object-oriented method-endmethod construct has its signature provided by its counterpart methods statement in the class definition component. Indeed, the concepts associated with defining and using local variables apply equally to both procedural subroutines and object-oriented methods.

The example in Listing 3-2 shows a class where all of its members belong to the instance realm . Accordingly, we would need to create an instance of this class in order to access these members. This is done via the create object statement:

report.
  o
  o
  o
    data         : rental_car     type ref to car.
  o
  o
  o
    create object rental_car.

Here we have defined a data field, rental_car, which defines an object reference variable to an object whose type is class car. Upon encountering the create object statement, an object of class car is created, somewhere in storage, by the runtime environment, and a reference to this storage is placed into the object reference variable rental_car. Once the object is created, it is through the object reference variable that we can access the object:

call method rental_car->set_year
  exporting year = '2014'.

The call method statement initiates access to a method of a class object through the object reference variable. In the preceding example statement, rental_car is the reference variable providing access to the car object, set_year is the name of a public method defined for the car object, and year is a parameter specified in the signature for the public method set_year. The -> symbol separating the name of the object reference variable from the name of the method is known as the object component selector and is used to access instance members of an object. There are variations on the statement to invoke methods of objects which are not covered here.32

An alternative to defining the car class, as shown in Listing 3-2, is to define the class such that all of its members belong to the static realm . In ABAP, the qualifier class-, prefixed to the data and methods statements in the definition component of the class, as shown highlighted in the Listing 3-3, consigns these members to the static realm .

Listing 3-3. Static Definition for the Car Class
class car definition.
  public section.
    class-methods: get_year exporting year type string
                 , set_year importing year type string
                 .
  private section.
    class-data   : model_year     type string
                 .
endclass.
class car implementation.
  method get_year.
    year                          = model_year.
  endmethod.
  method set_year.
    model_year                    = year.
  endmethod.
endclass.

Accordingly, the definition component of a class accommodates assigning both member visibility and member realm. Note that this now relegates the car class as a static encapsulation unit, one for which multiple instances are not possible and for which the create object statement is not applicable. A class defined this way, a static class, is one that is immediately available for use in a program (it does not require instantiation) and its behaviors also can be accessed by a call method statement, but the syntax of the call method statement differs in two significant aspects:

  • Members of the class are accessed simply via the name of the class instead of through an object reference variable .

  • In place of the object component selector, a class component selector (=>) is used to access the static members of the class.

Here is the same call method statement we saw before, altered accordingly for accessing a member of a static class, with differences highlighted:

call method car=>set_year
  exporting year = '2014'.

It is easy for us to see that the example in Listing 3-3 defines a static class; every one of its members is relegated to the static realm , with its single attribute defined using class-data and each of its two behaviors defined using class-methods. If we were to have even one member defined to the instance realm , then the class could no longer be considered a static class. Once a class has a significant number of members defined for it, determining whether or not it is a static class becomes more difficult through the process of checking whether or not there is at least one instance member. Imagine a class with over 100 attributes and more than that many methods33. It would be easy to miss a single instance member buried among such a large class definition, and a few years later, when we find ourselves maintaining this class, we long ago would have forgotten whether or not we had defined it as a static class. A way to determine this instantly is to apply some optional qualifiers on the class definition statement:

class class_name definition abstract final.

Here we see the additional qualifiers called abstract and final. Abstract indicates that the class cannot be instantiated. Final indicates that the class cannot have any subclasses34. Together they insure that there can be no instantiations of the class. Applying these two qualifiers to a class intended to function as a static class is an easy way both to guarantee as well as to document that the class is indeed a static class. It also alleviates the programmer from the tedious and potentially inaccurate process of visually applying a full body scan of the class definition only to arrive at the same conclusion.

The ABAP language provides for both instance and static constructors , as shown highlighted in Listing 3-4 of the car class, which has both instance and static members.

Listing 3-4. Car Class with Both Static and Instance Members
class car definition.
  public section.
    class-methods: class_constructor
                 , get_next_serial_number
                            exporting next_serial_number type i
                 .
    methods      : constructor
                 , get_year exporting year type string
                 , set_year importing year type string
                 .
  private section.
    class-data   : last_used_serial_number
                                  type i
                 .
    data         : serial_number  type i
                 , model_year     type string
                 .
endclass.
class car implementation.
  method class_constructor.
    last_used_serial_number       = 1000.
  endmethod.
  method constructor.
    call method get_next_serial_number
      importing
        next_serial_number        = serial_number.
  endmethod.
  method get_next_serial_number.
    add 01 to last_used_serial_number.
    serial_number                 = last_used_serial_number.
  endmethod.
  method get_year.
    year                          = model_year.
  endmethod.
  method set_year.
    model_year                    = year.
  endmethod.
endclass.

As illustrated in Listing 3-4, in addition to being defined to contain only instance members or only static members, a class can also be defined as a hybrid, containing a mix of both static and instance members.

Local method variables are defined within the bounding method and endmethod scope terminators , similar to how they would be defined between the procedural form and endform scope terminators:

method validate_registration.
  data         : is_registered  type abap_bool.
    o
    o
    o
  if licence_plate is not initial.
    is_registered = abap_true.
  endif.
    o
    o
    o
endmethod.

When specified for a class, ABAP requires the programmer to use the names constructor and class_constructor for the instance and static constructors, respectively. The static constructor appears on a class-methods statement in the public visibility section and must have no signature. The instance constructor appears on a methods statement in an appropriate visibility section35 and may have a signature.

In the example, the presence of a static constructor will cause the corresponding method implementation to be invoked when the class is first accessed. As shown in Listing 3-4, the method class_constructor will set the static attribute last_used_serial_number to the value 1000. This simply initializes this field to a starting value before any instances of the class are created. The static constructor should be used for such setup tasks associated with its static attributes.

Meanwhile, the presence of an instance constructor will cause the corresponding method implementation to be invoked each time a new instance of the class is created. As shown in Listing 3-4, the constructor method will invoke method get_next_serial_number of class car to receive the serial number for the new car instance. Notice here that method get_next_serial_number is defined as a static method, and it references the static attribute last_used_serial_number. After incrementing the value of this static attribute, method get_next_serial_number returns the updated value to the caller via the method signature . Accordingly, because static attributes are shared across all instances of a class, each newly instantiated car object will get a unique serial number since the value used for the previous car instance will be left behind to be incremented for the next one.

Since ABAP provides no explicit support for destructors , the way to destroy an object that no longer is required, and as a consequence release the storage it occupies, is simply to clear the value in the corresponding object reference variable . The ABAP runtime environment automatically keeps track of all the active references to an object, and when it detects that an object no longer has any active references, it marks the object for garbage collection, a storage optimization feature of the runtime environment.

Finally, the ABAP language provides for a class to offer friendship to other classes by naming them on the class definition statement in a friends clause:

class car definition [global | local] friends <friend1 [friend2 ...] >.

Encapsulation Units in ABAP

Since the concept of the encapsulation unit is not new to ABAP, perhaps it would be helpful to distinguish between those encapsulation units typically associated with procedural (classic) ABAP and those that are new with object-oriented ABAP.

Table 3-3 shows on the left those encapsulation units available to procedural programming and on the right those new with object-oriented programming, along with the ABAP statements to access the encapsulation unit.

Table 3-3. Comparison of Procedural and Object-Oriented Encapsulation Units

Procedural (classic) encapsulation units

Object-oriented encapsulation units

Report/program (SUBMIT)

Class

Subroutines within report (PERFORM)

Method (CALL METHOD)

Function group (CALL FUNCTION)

Transaction/dialog (CALL TRANSACTION)

Screens (CALL SCREEN)

(No direct screen processing capability)36

It should be noted that most of the encapsulation units associated with procedural ABAP are available for use by the encapsulation units associated with object-oriented ABAP, and vice versa. That is, a method of a class may contain a PERFORM (local class only) or CALL FUNCTION statement to a procedural encapsulation unit, and a classic ABAP subroutine or function module may contain a CALL METHOD statement to an object-oriented encapsulation unit.

Managing Class Encapsulation Units in the ABAP Repository

The ABAP source code repository facilitates retaining objects containing the ABAP code describing classes using two different designations. One designation makes the described class available to all other objects contained within the ABAP source code repository; the other designation limits the availability of the described class exclusively to the compilation unit with which it is used. The ABAP developer chooses which designation to use based on whether the class has the potential to be used in multiple settings. Accordingly, each class definition will be designated either a global class or a local class.

A global class is created and maintained via the Class Builder (transaction SE24). Once defined and activated, the class becomes available for use by any other object in the ABAP repository. Using the Class Builder to build a class is analogous to using the Function Builder (transaction SE37) to build a function of a function group. Both the Class Builder and Function Builder guide the developer to define the class or function module using a form-based approach, through which the developer is presented a set of tabs enabling the assignment of some aspect of the class or function module. The Class Builder also has the option of a source code-based approach, enabling the developer to create and maintain the entire class definition on a single editor screen, and provides the capability to toggle between the form-based and source code-based approaches as desired.

A local class is created and maintained via one of the standard ABAP source code editors (e.g., SE38, SE80). Local classes can coexist in an object with other non-class code. A typical scenario is one where local classes are embedded in a classic procedural report program, either in the same object as the procedural code or in an INCLUDE object that is included along with the other components to compose a complete compilation unit. Once defined and activated, the local class becomes available to all other components in the same compilation unit. That is, when included with a report, the local class can be used by all the procedural components defined in the report, but it is unavailable to entities defined outside the boundaries of the report. Indeed, local classes may be defined within function groups and global classes, and when they are, their availability is restricted to the components within the function group or global class compilation unit. A common use of local classes is to define classes to facilitate ABAP unit testing, the automated unit testing feature provided as part of the ABAP workbench, since the ABAP unit feature is initiated only through the methods of local classes.

Whether using the global class source code-based editor or one of the standard ABAP editors to define a local class, in both cases the definition portion of the class must physically precede its implementation portion. When multiple local classes are defined in a single component, it is common for these two complementary portions of the class to be adjacent to each other. There are situations, however, where the definition and implementation portions defining a single class will be separated from each other by the definition portions of one or more other local classes. This usually becomes necessary when there is an interdependency between multiple local classes, and is a consequence of the ABAP compiler being a single-pass compiler37. For instance, a local physician class holds a reference to a local patient class, and the local patient class holds a reference to the local physician class; the local classes are mutually dependent. In cases such as this, it is necessary for the definition portion of both local classes to precede the implementation portion of either class.38 This anomaly does not apply to global classes since only a single global class may be contained within a global class compilation unit.

Orienting Ourselves After Having Traversed Encapsulation

So, we have traveled some distance along the path from Procedureton to Objectropolis, and now, having completed our traversal through the object-oriented district known as Encapsulation, we are familiar with its principles and can now speak the language spoken by the residents in this district. Since this is such a new place for us, it would be helpful to orient ourselves and determine where we are in relation to some other place with which we are more familiar. Accordingly, let’s determine how far away we are from a place we know so well in ABAP procedural programming: Function Groups. We have learned that static classes have many similarities with function groups, but we also know that classes offer other capabilities beyond just static classes.

Refer to the chart in Appendix A, illustrating the comparison between function groups and classes on how each one facilitates the capabilities of the principles of object-oriented programming. The first 10 rows show how these two programming formats support the principles of Encapsulation. Although function groups, a place more familiar to us than classes, do not support all of these principles, there is enough common ground for us to conclude we are not that far away from the district where we would find function groups to be prominent; the terrain is similar, but we find geographical features in the object-oriented landscape of encapsulation that we do not find in function groups.

Summary

In this chapter, we became more familiar with the object-oriented concept of Encapsulation, and that it facilitates adherence to the design principle known as Separation of Concerns. We now know about visibility, how it is applied to components of classes using words representing a scale of gradations from least to most visibility, and why this is such an important concept to understand. We also learned that members of classes can exist in one of two realms:

  • Static realm

  • Instance realm

We also learned about encapsulation units and that classes can be defined as purely static encapsulation units, as purely instance encapsulation units, or as a hybrid combination of both static and instance encapsulation units. The concepts of constructors and destructors were introduced, which also have an aspect of applying either to the static realm or the instance realm. We learned about friendship, a concept applicable only to some object-oriented environments, and that using it breaks encapsulation. We also learned some considerations for using the principle of encapsulation effectively:

  • Encapsulate repetition.

  • Encapsulate complexity.

  • Encapsulate what is likely to vary.

  • Encapsulation units should have only a single responsibility.

Encapsulation Exercises

Refer to Chapter 3 of 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 101 series: ZOOT101A through ZOOT101E.

Footnotes

1 American Heritage Dictionary of the English Language, 4th edition, Houghton Mifflin Company, 2000, p. 588.

2 In some programming languages, notably ABAP, such subroutines are accessible from outside the program unit, but also require the name of the program as a qualifier for access to them, as in

perform subroutine_b in program bnx0037 ...

This is now considered an obsolete ABAP programming technique and is discouraged for any new development efforts.

3 In some languages, the placement of the definition for a local variable has an effect on its availability within the subroutine, making it available only beyond the point at which it is defined. This generally is the case with ABAP, although some dynamic techniques enable access to fields defined subsequently.

6 Here we are using the term variable to refer to these entities; however, this also would include constants, type definitions, and any other variation of defining or assisting in defining a data field.

7 This visibility level is not available in all object-oriented environments. Java provides support for this visibility level. It was not available to ABAP with the first release containing object-oriented support, but evidence that it is becoming available can be found at https://help.sap.com/saphelp_nwpi71/helpdata/en/45/c2b44f23b352f5e10000000a1553f7/content.htm .

8 This will be explained in more detail in the section on Inheritance.

9 The format is borrowed from the chart available at http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html .

10 Friendship is a concept related to encapsulation and will be covered later in this chapter.

11 In ABAP, the so-called TOP include of function groups is a good example of this type of program segment.

12 In ABAP, the interaction with screen definitions is based implicitly upon defining global variables in the program with the same names as those defined for the screens.

13 In Smalltalk, these are known as class variables and methods. In Visual Basic, these are known as shared members because the term static has a wholly different meaning in this language.

14 As we shall see, the prefix “class-” is used to denote a static member in ABAP.

15 Except, perhaps, hotel security guards, who might not tolerate our adventures in lobby redecorating.

16 The concept that subroutine variables are initialized with every invocation is not unique to object-oriented languages. This also applies to local variables defined between form and endform in classical ABAP.

17 This even applies to recursive invocations of methods, where the values of local variables in an outer level of method invocation are not available to each subsequent inner level unless these values explicitly have been passed between these levels via the method signature.

18 Although it is not mandatory that each instance of a sales order item, or for that matter a pair of instances for any type of class, contain a unique combination of attribute values, it stands to reason that if any two of them had the same values, they could simply be consolidated into a single instance, which could indicate the aggregate quantity of the two instead of having separate identical instances.

19 The design pattern known as Singleton, covered in a subsequent chapter, is a better alternative to a static class, offering a way to define a class for which we expect one and only one instance to be available.

21 By simple function groups, I mean those that do not have function modules defined with the extra clauses for special operation, such as “destination,” “starting new task,” “in background task,” and “in update task,” as well as those that are written to facilitate special features such as BAPI and IDOC processing.

26 Some languages running in environments with garbage collection provide a clean-up method, such as Java’s finalize, which is invoked during the garbage collection phase, but it is indeterminate exactly when the corresponding object will be garbage collected. Meanwhile, destructors are invoked immediately as the object is being destroyed. The difference typically stems from the capability in the language explicitly to delete an object. Objects in C++ can be explicitly deleted, so a destructor method is applicable. Objects in Java and ABAP are not explicitly deleted.

27 A static constructor can be triggered well before the creation of the first instance of the class, but if not, then it is certainly triggered upon creation of the first instance of the class.

28 One language that does support static destructors is D, in which the static destructor for a class is invoked upon thread termination. See http://dlang.org/class.html#StaticDestructor .

30 I’ve lost count of the number of times I have encountered ABAP subroutines containing hundreds of lines of code where the execution of two or three long and complex algorithms are made mutually exclusive based on some simple logical condition. Instead of moving the algorithms to their own subroutines, the authors chose to leave them embedded in the conditional logic found in the subroutine. In cases like these, the conditional logic becomes lost amongst the hundreds of lines of algorithms.

32 Refer to Horst Keller and Sascha Krűger, ABAP Objects: ABAP Programming in SAP Netweaver, 2nd edition, Galileo Press, 2007.

33 Although such a class can exist, it represents a class that should be divided into multiple smaller classes.

34 I will cover subclasses later in the book.

35 There was a time when ABAP required the instance constructor to be defined only in the public visibility section. This no longer is the case. Refer to the ABAP language documentation for more detail.

36 Screen processing within object-oriented ABAP is provided through WebDynpro and its successors or through the use of function groups that encapsulate screen definitions and their processing.

38 We will see an example of where this becomes necessary in the chapter on the State design pattern (Chapter 22).

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

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