© James E. McDonough 2017

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

18. Iterator Design Pattern

James E. McDonough

(1)Pennington, New Jersey, USA

The next stop on our voyage through the Design Patterns galaxy takes us to the Iterator design pattern, another of the design patterns found in the GoF catalog. We will find this design pattern useful when we need to access the elements of a structure sequentially without having to know the representation of those elements.

One of the things we should endeavor to avoid when writing code is having our programs know too much about how other entities manager their information. These other entities expose this information externally by not adhering sufficiently to the principle of encapsulation . The Iterator design pattern assists us in enforcing encapsulation by enabling external entities to obtain information without needing to know anything about how that information is organized internally.

Programming on a Need-to-Know Basis

The Ajax Company provides financial services for other corporations. One of these services is to facilitate managing contributions to the pensions of employees of their corporate customers. These customers provide Ajax with an electronic address of a class instance representing their company, from which Ajax's proprietary pension contribution management software retrieves the pertinent information about each employee of the customer corporation as it manages the pension contributions by that corporation to its employees.

Ajax started with only a few small corporations as customers. Each of them provides electronic access to their list of employees, which has always been organized in a simple spreadsheet format. This has worked fine for quite a while. Over the years Ajax has added a few more small corporations as customers, and these companies were able to provide electronic access to their list of employees in the same spreadsheet format.

The sales organization of Ajax recently got a tip about a very large corporation that was considering using its services. This potential customer has a policy of organizing human resources information in the xml format. They have agreed to contract with Ajax for the pension contribution management if Ajax agrees to change its software to access their employee list in the xml format. Not willing to miss such a lucrative sales opportunity, the Ajax management pressed its software development staff to find a way to facilitate this. Eventually the upgraded software became available, which relies on conditional logic in the processing program checking the customer identifier to determine the format of the data to be accessed.

This worked fine for while … until one day when the program crashed while processing the pension contributions for one of the smaller Ajax customers. An investigation revealed that this small corporation no longer stored its employee data in a spreadsheet format, but recently migrated to the same xml format used by the large corporate customer. Since the software was already capable of processing this format, the change only required some more conditional logic to include this small company in the list of those that managed their data in xml format.

A crisis was averted, but it served as a wake-up call to Ajax management. Although its pension contribution management processing program was based on needing to know the format of the employee data of its customers, a change to that data format could be implemented at any time by any of its customers. Ajax needed a better way to facilitate accessing the list of employees of its customers, one that did not rely on any specific data format.

Iterator Helps Out

The Iterator design pattern is uniquely suited to enable sequential access by a caller to a list of items held by another entity without the requirement for the caller to know anything about the format of the list. The Iterator design pattern is categorized by GoF with a behavioral purpose and an object scope. The intent behind this design pattern is the following:

  • Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. 1

With Iterator, we do not need to access an entire list. We merely need to know whether or not there are any remaining entries in the list, and, if so, to be able to retrieve the next entry. Accordingly, the format of the list provided by the external entity becomes irrelevant. The Ajax customers can store their employee data in any format they see fit. The Ajax pension contribution management processing program merely needs to request the next employee entry from the list, if one remains to be processed.

The definitive phrase of the intent is without exposing, since Iterator facilitates the adherence of a class to the principle of encapsulation by allowing it to keep the underlying representation of its aggregates hidden from external entities, a representation that without an Iterator might require the class to assign public visibility to an aggregate for it to become accessible to those external entities. As a consequence, it promotes loose coupling because a caller does not need to know how a callee defines the underlying representation for an aggregate; the caller merely receives a new object that can provide aggregate elements one after the other using a representation declared by an interface available to both caller and callee.

Iterator makes use of these participants2 working in collaboration with each other:

  1. Iterator: Defines an interface for accessing and traversing elements

  2. ConcreteIterator: Implements the iterator interface; keeps track of the current position in the traversal of the aggregate

  3. Aggregate: Defines an interface for creating an iterator object

  4. ConcreteAggregate : Implements the iterator creation interface to return an instance of a ConcreteIterator

Essentially, a concrete aggregate creates an instance of a concrete iterator, which contains a list of items and a pointer to the current item. The concrete iterator provides method implementations for traversing the list of items. There are variations for the methods provided by the iterator interface, but what might be considered a minimum set of methods for the iterator is illustrated in the UML class diagram shown in Figure 18-1.

A447555_1_En_18_Fig1_HTML.jpg
Figure 18-1. UML class diagram for the Iterator design pattern

The client accesses the concrete aggregate through the aggregate interface to request the creation of an instance of an iterator, which the concrete aggregate creates and passes back to the client. The concrete iterator created by the concrete aggregate contains a copy of the item list and has a pointer to the next item in the list. The client then accesses the concrete iterator through the iterator interface to determine whether there is an item remaining on the list (via the hasNext method) and to retrieve the next item on the list (via the getNext method ). Although the client is able to retrieve sequentially the list items of the concrete iterator, throughout this entire process it remains oblivious to the way in which the items are stored, either the original items themselves or the copy of those items created for the new iterator instance.

More robust implementations of the Iterator design pattern could include additional abstract methods defined for the iterator and implemented by the concrete iterator, to enable such capabilities as resetting the pointer to the beginning of the list or the end of the list, and moving either forward or backward in the list.

Figure 18-2 shows the UML class diagram for Iterator adapted to the processing required by the Ajax Company for managing pension contributions for its customers.

A447555_1_En_18_Fig2_HTML.jpg
Figure 18-2. UML class diagram for the Iterator design pattern applied to pension processor scenario

Ajax has changed its pension management system software to retrieve employee information from its customers using the model described above. The Ajax pension processing program requests a list of employees from the customer personnel portal, which creates for the Ajax requester an iterator object containing a list of employees. The Ajax pension processing program can now simply issue the hasNext and getNext calls against the iterator to retrieve information about the next employee, oblivious to how the list entries are being managed either by the customer personnel department or by the iterator object it has been provided.

Iterator in ABAP

The following code shows how we can implement the Iterator design pattern UML class diagram illustrated in Figure 18-2 into functioning ABAP code. We start with a definition of the employee class, as shown in Listing 18-1. We don’t show the full definition and implementation of this class since that level of information it is not pertinent to the operation of the Iterator pattern, instead merely indicating that its definition precedes the definition of other classes that will reference it.

Listing 18-1. Class employee
class employee definition.
  o
  o
endclass.
class employee implementation.
  o
  o
endclass.

Next, shown in Listing 18-2, is the definition of the abstract class iterator, playing the role of the Iterator participant.

Listing 18-2. Abstract Class iterator
interface iterator.
    methods      : get_next
                     returning value(next)
                       type ref to object
                 , has_next
                     returning value(more)
                       type abap_bool
                 .
endinterface.

The abstract class iterator, shown above, indicates ref to object as the type of parameter returned by the get_next method. This means it can return a reference to any type of instance since all classes implicitly inherit from object. It is also used as the superclass for class employee_iterator, shown in Listing 18-3, a class playing the role of the ConcreteIterator participant and one that makes reference to the employee class previously defined in Listing 18-1.

Listing 18-3. Class employee_iterator
class employee_iterator definition.
  public section.
    interfaces   : iterator
                 .
    aliases      : get_next
                     for iterator∼get_next
                 , has_next
                     for iterator∼has_next
                 .
    types        : employee_list   type standard table
                                     of ref to employee
                 .
    methods      : constructor
                     importing employee_stack
                       type employee_iterator=>employee_list
                 .
  private section.
    data         : current_index type i
                 , employee_entries type i
                 , employee_stack type employee_iterator=>employee_list
                 .
endclass .
class employee_iterator implementation.
  method constructor.
    call method super->constructor.
    me->employee_stack = employee_stack.
    describe table me->employee_stack lines me->employee_entries.
    me->current_index = 1.
  endmethod.
  method get_next.
    read table me->employee_stack
          into next index me->current_index.
    add 1 to me->current_index.
  endmethod.
  method has_next.
    if me->current_index le me->employee_entries.
      more = abap_true.
    else.
      more = abap_false.
    endif.
  endmethod.
endclass.

Notice the presence of a constructor method, the processing for which will copy the list of references to employee objects from the signature parameter to private attribute employee_stack, and then will set instance attribute employee_entries to the number of entries in the employee_stack and set instance attribute current_index to 1, leaving the instance in a state where it can iterate through the stack from the first entry to the last.

This is followed by the definition of the customer_personnel_access abstract class, shown in Listing 18-4, which plays the role of the Aggregate participant.

Listing 18-4. Class customer_personnel_access
interface customer_personnel_access.
    methods      : create_employee_iterator
                     returning value(iterator) type ref to iterator
                 .
endinterface.

Next, shown in Listing 18-5, is the definition for class customer_personnel_portal, playing the role of the ConcreteAggregate participant, a class that inherits from customer_personnel_access.

Listing 18-5. Class customer_personnel_portal
class customer_personnel_portal definition.
  public section.


    interfaces   : customer_personnel_access
                 .
    aliases      : create_employee_iterator
                     for customer_personnel_access∼create_employee_iterator
                 .
endclass.
class customer_personnel_portal implementation.
  method create_employee_iterator.
    data         : employee_stack type standard table
                                    of ref to employee
                 .
    invoke processing capable of creating into employee_stack
      a list of references to employee objects
    create object iterator type employee_iterator
      exporting employee_stack = employee_stack.
  endmethod.
endclass.

Its implementation for method create_employee_iterator consists of getting a list of references to employee instances and using this list to create an instance of an employee_iterator.

The Ajax pension processor code can now iterate through the list of employees simply by using the iterator created for it by an instance of customer_personnel_access, as shown in Listing 18-6.

Listing 18-6. Class ajax_pension_processor
class ajax_pension_processor definition.
  public section.
    methods      : iterate
                 , handle_pension_contribution
                     importing
                       employee
                         type ref to employee
                 .
  private section.
    data       : aggregate     type ref to customer_personnel_access
               , iterator      type ref to iterator
               , next_object   type ref to object
               , next_employee type ref to employee
               .
  method handle_pension_contribution.
    perform pension contribution activities
  endmethod.
  method iterate.
    create object aggregate type customer_personnel_portal.
    iterator = aggregate->create_employee_iterator( ).
    while iterator->has_next( ) eq abap_true.
      next_object              = iterator->get_next( ).
      try.
        next_employee          ?= next_object.
        call me->handle_pension_contribution(next_employee).
      catch cx_sy_move_cast_error.
        continue. “ ignore this entry
      endtry.
    endwhile.
  endmethod.
endclass.

There are a few things to notice about this ajax_pension_processor class. First, notice that its method iterate creates into field aggregate , defined with static type ref to customer_personnel_access, an instance of class customer_personnel_portal. Accordingly, the static type of field aggregate and dynamic type of reference to which it points are not the same.

Next to notice is that it defines field next_object as type ref to object. This will be the receiving field with the call to the get_next method of the iterator instance since the signature of the get_next method indicates that it returns a reference to object. In ABAP, a field defined as reference to object can point to any object but can access none of its members. Accordingly, if we want to refer to this object as an employee, we first need to move the pointer to a field defined as type ref to employee. Field next_employee is defined this way.

Recall from the discussion on inheritance that moving a pointer from a more generalized reference field to a more specialized one constitutes a specializing cast; the move may or may not succeed depending on whether the dynamic type of the sending field is compatible with the static type of the receiving field. In this case, we expect it to work because all of the object references returned by the iterator are references to employees. Despite our expectation, the ABAP compiler will require that we use the move-cast operator (?=) to set field next_employee from field next_object. If the move does not succeed, the move-cast operator will throw a move-cast exception, specifically, one of type cx_sy_move_cast_error. Accordingly, if we want to intercept this exception, we need to embed the move-cast operation in a try-endtry block , one that indicates exception class cx_sy_move_cast_error on a catch clause. This was implemented in Listing 18-6, so should the move-cast operation ever fail, we have indicated to ignore the error and continue on with the next iteration.

The last thing to notice is the call to method has_next of the iterator instance. The signature of this method indicates that it will be returning a parameter rather than changing or exporting one. Such methods are known as functional methods and are capable of being used as the logical expression in conditional statements when the value being returned is compatible with the type of logical expression applicable to the conditional statement. In the case shown in Listing 18-6, it is used to determine when the while loop should terminate since eventually it will return a condition of false, ending the while loop.

It may appear to some that the Iterator design pattern represents too much fuss about accessing the entries of a list sequentially. After all, in ABAP we can simply loop through the entries in a table without requiring all these classes to do it for us. Yes, this is true, but the benefit of the pattern is not that it can access list entries sequentially so much that it can do it without violating the principle of encapsulation , and when it comes to maintaining source code, this may mean the difference between having to change only the component responsible for holding the table entries and also having to change all the external entities that are accessing that table.

Summary

In this chapter, we learned there are ways a class can make the elements of an internal structure available sequentially to external entities without compromising its adherence to the principle of encapsulation. The Iterator design pattern reduces the amount of information classes need to know about each other and enables the maintenance of such interacting classes to proceed independently from each other. We also learned how the Iterator design pattern promotes loose coupling between classes.

Iterator Exercises

Refer to Chapter 15 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 308 series: ZOOT308A through ZOOT308D.

Footnotes

1 GoF, p. 257.

2 GoF, p. 259.

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

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