Gyan Factory

Gyan Factory
SAP Technical Project Support

Tuesday, February 16, 2016

ABAP Objects Design Patterns – Iterator

What is an Iterator ?

Iterator design pattern is to provide a way to access the underlying object collection without exposing the underlying representations. Iterator decouples the logic to access the collection of objects out of the Collection object itselft. This decoupling provides added adavantage while traversing through the different type of collection objects.
Iterator also provides the flexibilty to traverse the collection in different ways based on the requirement. If we have position embeded within the Collection object itself, it wont allow us to achieve the multiple access in different ways. The iterator takes the responsibility for access out of the Collection object and put that within itself. The iterator provides a unique interface to access the underlying datastructure. Clients doesn’t need to know what type of collection object is being accessed.

Standard Classes – But not useful

Standard SAP has provided the classes, CL_OBJECT_COLLECTION for Object collection and CL_OBJECT_COLLECTION_ITERATOR for Iterator. The underlying interfaces are not generic enough for handing other type of collection options.
The biggest drawback of using these classes is:
  • Method GET_ITERATOR only returns the object type referrence to CL_OBJECT_COLLECTION_ITERATOR. This method should actually return the object type IF_OBJECT_COLLECTION_ITERATOR.
  • Implementation of REMOVE method in class CL_OBJECT_COLLECTION is not efficient
  • Required methods are not part of the interface like ADD, REMOVE etc.
So for this demo application, I would create similar interfaces and implementing classes which are more generic and can be used in different collection and iterator implementation.

UML

Lets check out this UML used for the demo application.
UML of Iterator Design Pattern in ABAP
Components used in this UML:
  • IF_ITERATOR – This provides the interface to access and traverse through the list. This component is anIterator. This object would have different methods to access the list in different manners.
  • LCL_ITERATOR – This Concrete Iterator implements the interface IF_ITERATOR. This object contains all the logic for list access.
  • IF_COLLECTION – This object has method to instantiate the iterator objects. This is also referred asAggregate. This interface contains all the methods for List manipulation like ADD, REMOVE, GET etc.
  • LCL_COLLECTION – Implements the interface IF_COLLECTION as a Concrete Aggregate. This object would have a logic to instantiate proper iterator object. This object would also implements the list operation methods. Iterator would use methods e.g. GET to access particular object from the list.

Code Lines

Lets checkout the Code lines on how to implement the Iterator Design Patterns using ABAP. Best way to understand this example is to implement the code in SAP and debugging it through.

Iterator Design Pattern Demo

 
REPORT znp_dp_iterator.
*
CLASS lcl_item DEFINITION.
  PUBLIC SECTION.
    METHODS: constructor IMPORTING iv_name TYPE STRING.
    DATA: v_name TYPE STRING READ-ONLY.
ENDCLASS.                    "lcl_item DEFINITION
*
CLASS lcl_item IMPLEMENTATION.
  METHOD constructor.
    v_name = iv_name.
  ENDMETHOD.                    "constructor
ENDCLASS.                    "lcl_item IMPLEMENTATION
*
INTERFACE if_collection DEFERRED.
*
INTERFACE if_iterator.
  METHODS: get_index RETURNING VALUE(INDEX) TYPE i,
           has_next  RETURNING VALUE(has_next) TYPE flag,
           get_next  RETURNING VALUE(object) TYPE REF TO OBJECT,
           first     RETURNING VALUE(object) TYPE REF TO OBJECT,
           set_step  IMPORTING VALUE(iv_step) TYPE i.
  DATA: v_step TYPE i.
  DATA: v_current TYPE i.
  DATA: o_collection TYPE REF TO if_collection.
ENDINTERFACE.                    "if_iterator IMPLEMENTATION
*
INTERFACE if_collection.
  METHODS: get_iterator RETURNING VALUE(iterator) TYPE REF TO if_iterator.
  METHODS: ADD    IMPORTING element TYPE REF TO OBJECT,
           remove IMPORTING element TYPE REF TO OBJECT,
           CLEAR,
           SIZE   RETURNING VALUE(SIZE) TYPE i,
           is_empty RETURNING VALUE(empty) TYPE flag,
           GET    IMPORTING INDEX TYPE i
                  RETURNING VALUE(object) TYPE REF TO OBJECT.
ENDINTERFACE.                    "if_collection IMPLEMENTATION
*
CLASS lcl_iterator DEFINITION.
  PUBLIC SECTION.
    INTERFACES: if_iterator.
    METHODS: constructor IMPORTING io_collection TYPE REF TO if_collection.
    ALIASES: get_index   FOR if_iterator~get_index,
             has_next    FOR if_iterator~has_next,
             get_next    FOR if_iterator~get_next,
             first       FOR if_iterator~first,
             set_step    FOR if_iterator~set_step.
 
  PRIVATE SECTION.
    ALIASES: v_step      FOR if_iterator~v_step,
             v_current   FOR if_iterator~v_current,
             o_collection FOR if_iterator~o_collection.
ENDCLASS.                    "lcl_iterator DEFINITION
*
CLASS lcl_collection DEFINITION.
  PUBLIC SECTION.
    INTERFACES: if_collection.
    DATA: i_items TYPE STANDARD TABLE OF REF TO OBJECT.
    ALIASES: get_iterator   FOR if_collection~get_iterator,
             ADD            FOR if_collection~ADD,
             remove         FOR if_collection~remove,
             CLEAR          FOR if_collection~CLEAR,
             SIZE           FOR if_collection~SIZE,
             is_empty       FOR if_collection~is_empty,
             GET            FOR if_collection~GET.
ENDCLASS.                    "lcl_collection DEFINITION
 
*
CLASS lcl_collection IMPLEMENTATION.
  METHOD if_collection~get_iterator.
    CREATE OBJECT iterator
      TYPE
        lcl_iterator
      EXPORTING
        io_collection = me.
  ENDMETHOD.                    "if_collection~get_iterator
  METHOD if_collection~ADD.
    APPEND element TO i_items.
  ENDMETHOD.                    "if_collection~add
  METHOD if_collection~remove.
    DELETE i_items WHERE TABLE_LINE EQ element.
  ENDMETHOD.                    "if_collection~remove
  METHOD if_collection~CLEAR.
    CLEAR: i_items.
  ENDMETHOD.                    "if_collection~clear
  METHOD if_collection~SIZE.
    SIZE = LINES( i_items ).
  ENDMETHOD.                    "if_collection~size
  METHOD if_collection~is_empty.
    IF me->size( ) IS INITIAL.
      empty = 'X'.
    ENDIF.
  ENDMETHOD.                    "if_collection~is_empty
  METHOD if_collection~GET.
    READ TABLE i_items INTO object INDEX INDEX.
  ENDMETHOD.                    "if_collection~get
ENDCLASS.                    "lcl_collection IMPLEMENTATION
*
CLASS lcl_iterator IMPLEMENTATION.
  METHOD constructor.
    o_collection = io_collection.
    v_step = 1.
  ENDMETHOD.                    "constructor
  METHOD if_iterator~first.
    v_current = 1.
    object = o_collection->get( v_current ).
  ENDMETHOD.                    "if_iterator~first
  METHOD if_iterator~get_next.
    v_current = v_current + v_step.
    object = o_collection->get( v_current ).
  ENDMETHOD.                    "if_iterator~next
  METHOD if_iterator~has_next.
    DATA obj TYPE REF TO OBJECT.
    DATA idx TYPE i.
    idx = v_current + v_step.
    obj = o_collection->get( idx ).
    IF obj IS BOUND.
      has_next = 'X'.
    ENDIF.
  ENDMETHOD.                    "if_iterator~isdone
  METHOD if_iterator~set_step.
    me->v_step = iv_step.
  ENDMETHOD.                    "if_iterator~SET_STEP
  METHOD if_iterator~get_index.
    INDEX = INDEX.
  ENDMETHOD.                    "if_iterator~get_index
ENDCLASS.                    "iterator IMPLEMENTATION
*
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: run.
ENDCLASS.                    "lcl_main DEFINITION
*
CLASS lcl_main IMPLEMENTATION.
  METHOD run.
*
    DATA: o_collection TYPE REF TO if_collection.
    DATA: o_iterator TYPE REF TO if_iterator.
    DATA: lo_item TYPE REF TO lcl_item.
 
    CREATE OBJECT o_collection TYPE lcl_collection.
    o_iterator = o_collection->get_iterator( ).
 
    CREATE OBJECT lo_item
      EXPORTING
        iv_name = 'Item1'.
    o_collection->add( lo_item ).
    CREATE OBJECT lo_item
      EXPORTING
        iv_name = 'Item2'.
    o_collection->add( lo_item ).
    CREATE OBJECT lo_item
      EXPORTING
        iv_name = 'Item3'.
    o_collection->add( lo_item ).
    CREATE OBJECT lo_item
      EXPORTING
        iv_name = 'Item4'.
    o_collection->add( lo_item ).
    CREATE OBJECT lo_item
      EXPORTING
        iv_name = 'Item5'.
    o_collection->add( lo_item ).
 
    "o_iterator->set_step( 2 ).
    WHILE o_iterator->has_next( ) IS NOT INITIAL.
      lo_item ?= o_iterator->get_next( ).
      WRITE: / lo_item->v_name.
    ENDWHILE.
  ENDMETHOD.                    "run
ENDCLASS.                    "lcl_main IMPLEMENTATION
 
START-OF-SELECTION.
  lcl_main=>run( ).
 

Should we implement Iterator in ABAP?

At first its seems too much overhead implementing the Iterator Design Pattern in ABAP as we have Iternal Tables. We can create internal tables which can contain the Objects and perform the operations like reading an entry, adding new entries, deleting entries etc. To implement the iterator design pattern, we need to create a separate Iterator object which would traverse through the collected objects in the collection. This collection would be generally the ITAB containg all the objects.
We should try to implement Iterator because,
  • We could create as many iterator as we want to traverse objects in the different sequence.
  • Linked List Object collection – When we need to use the Linked List type of the object collection, it would be difficult for every client to implement the accessing algorithm. Rather than that, we can create the Iterator and all clients can straight away use Iterator to access any object collection – ITAB or Linked List.
Do you agree on this?

No comments:

Post a Comment