© James E. McDonough 2017

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

26. Visitor 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 Visitor design pattern, another of the design patterns found in the GoF catalog. We will find this design pattern most useful once the maintenance cycle has begun on software components.

Before exploring how the Visitor Design Pattern can make our programming lives easier, let’s first consider an example of the problem it solves.

Implementation of a New City-Wide Safety Policy

The Public Works Department of the city of Galveston, New York recently implemented a sweeping new safety policy whereby all buildings within the city limits are to be inspected once every five years. A central database retains the addresses of each building along with the inspection certificates and date granted for each type of inspection performed for the building.

When first implemented, the types of inspections to be performed were limited to water quality and air quality. The types of buildings to be inspected were divided into three categories: residential, commercial, and manufacturing. Each type of building had its own requirements for a complete inspection. A residential building inspection usually took one hour; a commercial building inspection required three hours to complete; and a manufacturing building inspection lasted eight hours.

Each business day a Public Works Department agent runs a computer program where the city street and type of inspection is specified on an initial selection screen; the database is searched for the buildings on that street where the most recent inspection date for the specified type of inspection is older than five years; and the resulting addresses are subjected to a new inspection.

Inspections-R-Us

The Ace Business Services Corporation was hired to build the software to facilitate this new safety policy. The designers were fluent in object-oriented principles and immediately recognized the three types of buildings as specializations of a building class. Figure 26-1 shows the UML class diagram they devised to illustrate the hierarchical relationship between these classes.

A447555_1_En_26_Fig1_HTML.jpg
Figure 26-1. UML class diagram showing hierarchy between classes

Here we see an abstract class named building, which has a private attribute inspection_certificates, implemented public methods receive_inspection_certificate and get_last_inspection_date, and abstract public methods inspect_water_quality and inspect_air_quality. Classes residential_building, commercial_building, and manufacturing_building each inherit from class building, with each one implementing the abstract methods inspect_water_quality and inspect_air_quality defined by their superclass. One of the programmers quickly wrote some code resembling the ABAP-style pseudocode for the abstract building class, as shown in Listing 26-1.

Listing 26-1. Abstract Class building
class building definition abstract.
  public section.
    types        : certificates           type string
                 .
    methods      : receive_inspection_certificate
                     importing inspection type string
                               date       type sydatum
                 , get_last_inspection_date
                     importing inspection type string
                     exporting date       type sydatum
                 , inspect_water_quality abstract
                     returning value(assessment) type char10
                 , inspect_air_quality abstract
                     returning value(assessment) type char10
                 .
  private section.
    data         : inspection_certificates
                     type standard table of certificates.
endclass.
class building implementation.
  method receive_inspection_certificate.
    o
    o
  endmethod.
  method get_last_inspection_date.
    o
    o
  endmethod.
endclass.

The abstract class building shown in Listing 26-1 provides implementations for some of its methods but defers those specific to a type of inspection to subclasses. Shown below are the three classes inheriting from the building class. Listing 26-2 shows the classes inheriting from abstract class building; all are virtually identical in format, the significant difference being the processing involved for each type of inspection.

Listing 26-2. Classes Inheriting from Abstract Class building
class residential_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 .
endclass.
class residential_building implementation.
  method inspect_water_quality.
    assessment = perform one-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform one-hour air quality inspection
  endmethod.
endclass.


class commercial_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 .
endclass.
class commercial_building implementation.
  method inspect_water_quality.
    assessment = perform three-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform three-hour air quality inspection
  endmethod.
endclass.


class manufacturing_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 .
endclass.
class manufacturing_building implementation.
  method inspect_water_quality.
    assessment = perform eight-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform eight-hour air quality inspection
  endmethod.
endclass.

Listing 26-3 shows a snippet of code to facilitate inspections by invoking the objects described above.

Listing 26-3. Code for a Component Invoking the Objects Described in Preceding Listings
  o
  o
  data street                     type string.
  data inspection_type            type string.
  data last_inspection_date       type sydatum.
  data assessment                 type char10.
  data buildings                  type table of ref to building.
  data building                   type          ref to building.
  o
  o
  get inspection_type from selection screen
  get street          from selection screen
  create into buildings instances of all buildings on street
  loop at buildings into building.
    call method building->get_last_inspection
      exporting inspection        = inspection_type
      importing date              = last_inspection_date.
    if last_inspection_date older than five years ago.
      case inspection_type.
        when 'water_quality'.
          assessment = building->inspect_water_quality( ).
        when 'air_quality'.
          assessment = building->inspect_air_quality( ).
      endcase.
      if assessment = 'passed'.
        call method building->receive_inspection_certificate
          exporting inspection     = inspection_type
          date                     = sy-datum.
      endif.
    endif.
  endloop.

This design worked very well for a while at the Public Works Department in Galveston, New York, but, after a new town council was elected, the decision was made to expand the inspection policy to include inspections for insect infestations. Ace Business Services was awarded the contract to make the necessary changes to the software, and after some brainstorming they drew the UML diagram shown in Figure 26-2, with the differences from the UML diagram in Figure 26-1 highlighted.

A447555_1_En_26_Fig2_HTML.jpg
Figure 26-2. UML class diagram showing how the building class hierarchy handles an additional method, with differences from Figure 26-1 highlighted

Here we see new abstract method named inspect_for_insect_infestation added to the building superclass, with each of the subclasses providing an implementation for it. Listing 26-4 shows the corresponding code for abstract class building, with differences from Listing 26-1 highlighted.

Listing 26-4. Abstract Class building, with Differences from Listing 26-1 Highlighted
class building definition abstract.
  public section.
    types        : certificates           type string
                 .
    methods      : receive_inspection_certificate
                     importing inspection type string
                               date       type sydatum
                 , get_last_inspection_date
                     importing inspection type string
                     exporting date       type sydatum
                 , inspect_water_quality abstract
                     returning value(assessment) type char10
                 , inspect_air_quality abstract
                     returning value(assessment) type char10
                 , inspect_for_insect_infestation abstract
                     returning value(assessment) type char10
                 .
  private section.
    data         : inspection_certificates
                     type standard table of certificates.
endclass.
class building implementation.
  method receive_inspection_certificate.
    o
    o
  endmethod.
  method get_last_inspection_date.
    o
    o
  endmethod.
endclass.

Class building is changed to include a new abstract method for insect infestation inspections. This is implemented in the inheriting classes shown in Listing 26-5, with differences from Listing 26-2 highlighted.

Listing 26-5. Classes Inheriting from Abstract Class building, with Differences from Listing 26-2 Highlighted
class residential_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 .
endclass.
class residential_building implementation.
  method inspect_water_quality.
    assessment = perform one-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform one-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform one-hour insect infestation inspection
  endmethod.
endclass.


class commercial_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 .
endclass.
class commercial_building implementation.
  method inspect_water_quality.
    assessment = perform three-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform three-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform three-hour insect infestation inspection
  endmethod.
endclass.


class manufacturing_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 .
endclass.
class manufacturing_building implementation.
  method inspect_water_quality.
    assessment = perform eight-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform eight-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform eight-hour insect infestation inspection
  endmethod.
endclass.

Listing 26-6 shows the invoking snippet of code, also changed to accommodate the new type of inspection, with differences from Listing 26-3 highlighted.

Listing 26-6. Code for a Component Invoking the Objects Described in Preceding Listings, with Differences from Listing 26-3 Highlighted
o
o
data street                     type string.
data inspection_type            type string.
data last_inspection_date       type sydatum.
data assessment                 type char10.
data buildings                  type table of ref to building.
data building                   type          ref to building.
o
o
get inspection_type from selection screen
get street          from selection screen
create into buildings instances of all buildings on street
loop at buildings into building.
  call method building->get_last_inspection
    exporting inspection        = inspection_type
    importing date              = last_inspection_date.
  if last_inspection_date older than five years ago.
    case inspection_type.
      when 'water_quality'.
        assessment = building->inspect_water_quality( ).
      when 'air_quality'.
        assessment = building->inspect_air_quality( ).
      when 'insect_infestation'.
        assessment = building->inspect_for_insect_infestation( ).
    endcase.
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_type
        date                     = sy-datum.
    endif.
  endif.
endloop.

This was not difficult for the Ace programmers, but a closer examination reveals that changes to facilitate one new type of inspection resulted in modifications to four classes and one program; indeed, the entire set of components required changes.

The following year the town council of Galveston, New York passed a resolution to include a fire safety inspection in the Public Works Department building inspection policy. Again Ace Business Services was awarded the contract to make the changes and after some brainstorming they drew the UML diagram shown in Figure 26-3, with the differences from the UML diagram in Figure 26-2 highlighted.

A447555_1_En_26_Fig3_HTML.jpg
Figure 26-3. UML class diagram showing how the building class hierarchy handles an additional method, with differences from Figure 26-2 highlighted

Here we see new abstract method inspect_fire_safety added to the building superclass, with each of the subclasses providing an implementation for it. Listing 26-7 shows the corresponding code for abstract class building, with differences from Listing 26-4 highlighted.

Listing 26-7. Abstract Class building, with Differences from Listing 26-4 Highlighted
class building definition abstract.
  public section.
    types        : certificates           type string
                 .
    methods      : receive_inspection_certificate
                     importing inspection type string
                               date       type sydatum
                 , get_last_inspection_date
                     importing inspection type string
                     exporting date       type sydatum
                 , inspect_water_quality abstract
                     returning value(assessment) type char10
                 , inspect_air_quality abstract
                     returning value(assessment) type char10
                 , inspect_for_insect_infestation abstract
                     returning value(assessment) type char10
                 , inspect_for_fire_safety abstract
                     returning value(assessment) type char10
                 .
  private section.
    data         : inspection_certificates
                     type standard table of certificates.
endclass.
class building implementation.
  method receive_inspection_certificate.
    o
    o
  endmethod.
  method get_last_inspection_date.
    o
    o
  endmethod.
endclass.

Again, class building is changed to include a new abstract method for fire safety inspections. This is implemented in the inheriting classes shown in Listing 26-8, with differences from Listing 26-5 highlighted.

Listing 26-8. Classes Inheriting from Abstract Class building, with Differences from Listing 26-5 Highlighted
class residential_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 , inspect_for_fire_safety redefinition
                 .
endclass.
class residential_building implementation.
  method inspect_water_quality.
    assessment = perform one-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform one-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform one-hour insect infestation inspection
  endmethod.
  method inspect_for_fire_safety.
    assessment = perform one-hour fire safety inspection
  endmethod.
endclass.


class commercial_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 , inspect_for_fire_safety redefinition
                 .
endclass.
class commercial_building implementation.
  method inspect_water_quality.
    assessment = perform three-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform three-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform three-hour insect infestation inspection
  endmethod.
  method inspect_for_fire_safety.
    assessment = perform three-hour fire safety inspection
  endmethod.
endclass.


class manufacturing_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 , inspect_for_fire_safety redefinition
                 .
endclass.
class manufacturing_building implementation.
  method inspect_water_quality.
    assessment = perform eight-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform eight-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform eight-hour insect infestation inspection
  endmethod.
  method inspect_for_fire_safety.
    assessment = perform eight-hour fire safety inspection
  endmethod.
endclass.

Listing 26-9 shows the invoking snippet of code, also changed to accommodate the new type of inspection, with differences from Listing 26-6 highlighted.

Listing 26-9. Code for a Component Invoking the Objects Described in Preceding Listings, with Differences from Listing 26-6 Highlighted
o
o
data street                     type string.
data inspection_type            type string.
data last_inspection_date       type sydatum.
data assessment                 type char10.
data buildings                  type table of ref to building.
data building                   type          ref to building.
o
o
get inspection_type from selection screen
get street          from selection screen
create into buildings instances of all buildings on street
loop at buildings into building.
  call method building->get_last_inspection
    exporting inspection        = inspection_type
    importing date              = last_inspection_date.
  if last_inspection_date older than five years ago.
    case inspection_type.
      when 'water_quality'.
        assessment = building->inspect_water_quality( ).
      when 'air_quality'.
        assessment = building->inspect_air_quality( ).
      when 'insect_infestation'.
        assessment = building->inspect_for_insect_infestation( ).
      when 'fire_safety'.
        assessment = building->inspect_for_fire_safety( ).
    endcase.
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_type
        date                     = sy-datum.
    endif.
  endif.
endloop.

This task wasn’t difficult for the Ace programmers, but yet again the entire set of components required changes. This design now raised a red flag with one of the Ace programmers, who began to wonder whether the implementation for any subsequent inspection types would also require every component to change.

Sorry, We’re Closed

Applying changes to existing components violates what is known as the Open/Closed Principle, 1 another of the principles defined by Robert C. Martin. The principle states that once a component has been placed into production, from that point forward it should be open for extension but closed for modification . Extension means the component can be used as a superclass to a new component seeking to specialize what the component does.2 Accordingly, to introduce new processing we can use an existing component as a superclass (it is open for extension) for creating a new class, but we should avoid making changes to the existing component itself (it is closed for modification).

This principle recognizes the reality that it is easier and safer not to change existing code, code that presumably has already been thoroughly tested prior to being placed into production; to change existing components risks introducing new bugs and possibly making the code harder to understand.

Just Visiting

The Visitor design pattern enables us to more easily abide by the Open/Closed Principle. Once in place, it simplifies our maintenance efforts when, as we saw with the addition of the insect infestation and fire safety inspections noted above, we need to add new capabilities to an existing process.

The Visitor design pattern is categorized by GoF with a behavioral purpose and an object scope. The intent behind this design pattern is the following:

  • Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. 3

Visitor makes use of these participants4 working in collaboration with each other:

  1. Visitor: Declares a Visit operation for each class of ConcreteElement in the object structure. The operation's name and signature identifies the class that sends the Visit request to the visitor. That lets the visitor determine the concrete class of the element being visited. Then the visitor can access the element directly through its particular interface.

  2. ConcreteVisitor: Implements each operation declared by Visitor. Each operation implements a fragment of the algorithm defined for the corresponding class of object in the structure. ConcreteVisitor provides the context for the algorithm and stores its local state. This state often accumulates results during the traversal of the structure.

  3. Element: Defines an Accept operation that takes a visitor as an argument.

  4. ConcreteElement: Implements an Accept operation that takes a visitor as an argument.

  5. ObjectStructure: Can enumerate its elements. May provide a high-level interface to allow the visitor to visit its elements. May either be a composite or a collection such as a list or a set.

The UML class diagram for the Visitor design pattern is shown in Figure 26-4.

A447555_1_En_26_Fig4_HTML.jpg
Figure 26-4. UML class diagram for the Visitor design pattern

Here we see a client that has an object structure and uses a visitor. The visitor is an interface implemented by concrete visitors. Each concrete visitor has methods to visit each type of concrete element. The object structure also has elements. Each element is an interface implemented by concrete elements. Each of the concrete elements provides an implementation for the Accept method, which is simply to invoke its corresponding visitConcreteElementX method of the visitor interface reference it has been provided.

It may be helpful to think of the element participant as a visitable element . Accordingly, a client has an object structure composed of visitable elements, and uses visitors to visit its visitable elements. The visitor interface provides a visitConcreteElementX method for each one of the classes implementing the visitable element interface. As such, each concrete visitor has as many visitConcreteElementX method implementations as there are concreteElement classes, one method implementation to correspond to each visitable element class.

The client obtains a reference to a visitor interface. It then loops through its object structure, and for each visitable element invokes its accept method, passing it a reference to the visitor object. The implementation of the accept method of the concrete visitable element, in turn, invokes the relevant visitConcreteElementX method of the visitor object, passing it a reference to the concrete visitable element object making the invocation.

Recall from the discussion on polymorphism that dynamic dispatch is the process by which the dynamic type of an object reference is used at execution time to determine the actual method implementation to be invoked. The Visitor design pattern uses what is known as double dynamic dispatch . Here is how it works:

  • The first dynamic dispatch occurs when the client invokes method accept of the visitable element object, causing the dynamic type of the visitable element interface reference to determine the specific concrete visitable element object whose accept method implementation is to be invoked.

  • The second dynamic dispatch occurs when the implementation of the accept method of the visitable element object invokes method visitConcreteElementX of the visitor object it had been passed, causing the dynamic type of the visitor interface reference to determine the specific concrete visitor object whose visitConcreteElementX method is to be invoked.

Accordingly, it is the combination of both the dynamic type of the visitable object and the dynamic type of the visitor object that eventually determines the processing to be performed.

Visiting Hours

Once the Visitor design pattern was brought to their attention, the developers at Ace Business Services spent a few hours devising a new UML class diagram, shown in Figure 26-5, which they intend to use to redesign the processing for the Public Works Department of Galveston, New York so that any additional requests for new types of inspections no longer require changes to existing components.

A447555_1_En_26_Fig5_HTML.jpg
Figure 26-5. UML class diagram for the Visitor design pattern applied to the building inspection scenario

Compared with the general UML class diagram for the Visitor design pattern shown in Figure 26-4, we see here exactly the same relationship between the client (inspection_program), objectStructure (buildings_to_inspect), and visitor interface: the inspection_program uses the visitor interface and the inspection_program has a structure of buildings_to_inspect. The element interface of the general Visitor UML class diagram shown in Figure 26-4 is represented here as the visitable interface, but otherwise has the same relationship to the objectStructure (buildings_to_inspect). The visitor interface defines three visit methods, each of which corresponds to one of the subclasses of building: residential, commercial, and manufacturing. Classes residential_building, commercial_building, and manufacturing_building, all inheriting from superclass building, correspond to the concreteElementX classes of the general Visitor UML class diagram. Classes water_quality_inspector and air_quality_inspector, each implementing the visitor interface, correspond to the concreteVisitorN classes of the general Visitor UML class diagram shown in Figure 26-4.

In the diagram in Figure 26-5, the building class is shown implementing the visitable interface but does not provide implementations for the accept method defined by visitable. Instead, the diagram shows that class building delegates to its subclasses the responsibility to provide an implementation for the accept method.

Indeed, while class building still defines and provides implementations for methods receive_inspection_certificate and get_last_inspection_date, it no longer defines abstract methods inspect_water_quality, inspect_air_quality, etc.5 These methods have been rendered obsolete by the presence of the corresponding new concrete visitor classes water_quality_inspector, air_quality_inspector, etc.

Visitor in ABAP

The Ace developers also rewrote the components to make use of the Visitor design pattern. Listing 26-10 shows the new interfaces, visitor and visitable , facilitating the Visitor design pattern. Notice the presence of the class definition deferred statement naming the building class, referenced by the visitor interface but at this point not yet encountered by the ABAP compiler (it is defined in Listing 26-11).

Listing 26-10. Interfaces Visitor and Visitable
class building definition deferred.

interface visitor.
  methods        : visit_residential_building
                     importing building type ref to building
                 , visit_commercial_building
                     importing building type ref to building
                 , visit_manufacturing_building
                     importing building type ref to building
                 .
endinterface.


interface visitable.
  methods        : accept
                     importing visitor type ref to visitor.
endinterface.

Interface visitor plays the role of the Visitor participant, while interface visitable plays the role of the Element participant. Listing 26-11 shows how abstract class building is changed to indicate that it implements interface visitable, with differences from Listing 26-7 highlighted.

Listing 26-11. Abstract Class building, with Differences from Listing 26-7 Highlighted
class building definition abstract.
  public section.
    interfaces   : visitable all methods abstract.
    aliases      : accept for visitable∼accept.
    types        : certificates           type string
                 .
    methods      : receive_inspection_certificate
                     importing inspection type string
                               date       type sydatum
                 , get_last_inspection_date
                     importing inspection type string
                     exporting date       type sydatum
                 , inspect_water_quality abstract
                     returning value(assessment) type char10
                 , inspect_air_quality abstract
                     returning value(assessment) type char10
                 , inspect_for_insect_infestation abstract
                     returning value(assessment) type char10
                 , inspect_for_fire_safety abstract
                     returning value(assessment) type char10
                 .
  private section.
    data         : inspection_certificates
                     type standard table of certificates.
endclass.
class building implementation.
  method receive_inspection_certificate.
    o
    o
  endmethod.
  method get_last_inspection_date.
    o
    o
  endmethod.
endclass.

Abstract class building plays the role of a superclass to a ConcreteElement participant. Notice that all of its previous abstract methods have been eliminated. Notice also that the method definitions it gets by implementing the visitable interface are marked abstract, through the “… all methods abstract” clause of the interfaces statement, meaning implementations for these methods are delegated to the subclasses.

Listing 26-12 shows how the three inheriting subclasses no longer provide implementations for the abstract methods eliminated from the superclass, with differences from Listing 26-8 highlighted. Each one, playing the role of a ConcreteElement participant, provides an implementation for the accept method, which, using the visitor instance provided by the caller through the signature of the accept method, invokes a method of the visitor instance, providing itself, through the self-reference variable me, as the visitable entity .

Listing 26-12. Classes Inheriting from Abstract Class building, with Differences from Listing 26-8 Highlighted
class residential_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 , inspect_for_fire_safety redefinition
    methods      : accept redefinition
                 .
endclass.
class residential_building implementation.
  method inspect_water_quality.
    assessment = perform one-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform one-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform one-hour insect infestation inspection
  endmethod.
  method inspect_for_fire_safety.
    assessment = perform one-hour fire safety inspection
  endmethod.
  method accept.
    visitor->visit_residential_building( me ).
  endmethod.
endclass.


class commercial_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 , inspect_for_fire_safety redefinition
    methods      : accept redefinition
                 .
endclass.
class commercial_building implementation.
  method inspect_water_quality.
    assessment = perform three-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform three-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform three-hour insect infestation inspection
  endmethod.
  method inspect_for_fire_safety.
    assessment = perform three-hour fire safety inspection
  endmethod.
  method accept.
    visitor->visit_commercial_building( me ).
  endmethod.
endclass.


class manufacturing_building definition inheriting from building.
  public section.
    methods      : inspect_water_quality redefinition
                 , inspect_air_quality redefinition
                 , inspect_for_insect_infestation redefinition
                 , inspect_for_fire_safety redefinition
    methods      : accept redefinition
                 .
endclass.
class manufacturing_building implementation.
  method inspect_water_quality.
    assessment = perform eight-hour water quality inspection
  endmethod.
  method inspect_air_quality.
    assessment = perform eight-hour air quality inspection
  endmethod.
  method inspect_for_insect_infestation.
    assessment = perform eight-hour insect infestation inspection
  endmethod.
  method inspect_for_fire_safety.
    assessment = perform eight-hour fire safety inspection
  endmethod.
  method accept.
    visitor->visit_manufacturing_building( me ).
  endmethod.
endclass.

Notice that implementations for the abstract methods previously defined by class building have been eliminated, replaced with implementations for method accept that class building now acquires by implementing the visitable interface. As a consequence, the inspection activity previously performed by the building subclasses has been transferred to the new concrete visitor subclasses, shown in Listing 26-13, where each of the different types of inspections now becomes a class implementing the visitor interface, each playing the role of the ConcreteVisitor participant.

Listing 26-13. Classes Implementing the Visitor Interface
class water_quality_inspector definition.
  public section.
    interfaces   : visitor.
    aliases      : visit_residential_building
                     for visitor∼visit_residential_building
                 , visit_commercial_building
                     for visitor∼visit_commercial_building
                 , visit_manufacturing_building
                     for visitor∼visit_manufacturing_building
                 .
  private section.
    constants    : inspection_specialization
                     type string value `water quality`.
endclass.
class water_quality_inspector implementation.
  method visit_residential_building.
    data assessment               type char10.
    assessment = perform one-hour water quality inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_commercial_building.
    data assessment               type char10.
    assessment = perform three-hour water quality inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_manufacturing_building.
    data assessment               type char10.
    assessment = perform eight-hour water quality inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
endclass.


class air_quality_inspector definition.
  public section.
    interfaces   : visitor.
    aliases      : visit_residential_building
                     for visitor∼visit_residential_building
                 , visit_commercial_building
                     for visitor∼visit_commercial_building
                 , visit_manufacturing_building
                     for visitor∼visit_manufacturing_building
                 .
  private section.
    constants    : inspection_specialization
                     type string value `air quality`.
endclass.
class air_quality_inspector implementation.
  method visit_residential_building.
    data assessment               type char10.
    assessment = perform one-hour air quality inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_commercial_building.
    data assessment               type char10.
    assessment = perform three-hour air quality inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_manufacturing_building.
    data assessment               type char10.
    assessment = perform eight-hour air quality inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
endclass.


class insect_infestation_inspector definition.
  public section.
    interfaces   : visitor.
    aliases      : visit_residential_building
                     for visitor∼visit_residential_building
                 , visit_commercial_building
                     for visitor∼visit_commercial_building
                 , visit_manufacturing_building
                     for visitor∼visit_manufacturing_building
                 .
  private section.
    constants    : inspection_specialization
                     type string value `insect infestation`.
endclass.
class insect_infestation_inspector implementation.
  method visit_residential_building.
    data assessment               type char10.
    assessment = perform one-hour insect infestation inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_commercial_building.
    data assessment               type char10.
    assessment = perform three-hour insect infestation inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_manufacturing_building.
    data assessment               type char10.
    assessment = perform eight-hour insect infestation inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
endclass.


class fire_safety_inspector definition.
  public section.
    interfaces   : visitor.
    aliases      : visit_residential_building
                     for visitor∼visit_residential_building
                 , visit_commercial_building
                     for visitor∼visit_commercial_building
                 , visit_manufacturing_building
                     for visitor∼visit_manufacturing_building
                 .
  private section.
    constants    : inspection_specialization
                     type string value `fire safety`.
endclass.
class fire_safety_inspector implementation.
  method visit_residential_building.
    data assessment               type char10.
    assessment = perform one-hour fire safety inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_commercial_building.
    data assessment               type char10.
    assessment = perform three-hour fire safety inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_manufacturing_building.
    data assessment               type char10.
    assessment = perform eight-hour fire safety inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
endclass.

Notice the similarity between the classes implementing the visitor interface shown above. They are virtually identical except for the value specified for the inspection specialization each one handles and the way each one resolves a value for the assessment of an inspection.

The invoking snippet of code, shown in Listing 26-14, is changed to facilitate the Visitor design pattern, with differences from Listing 26-9 highlighted. It plays the role of both the Client participant, since it creates the inspector, an instance of a class implementing the visitor interface, and the ObjectStructure participant, for containing the definition of the table of references to building instances over which the inspector will visit.

Listing 26-14. Code for a Component Invoking the Objects Described in Preceding Listings, with Differences from Listing 26-9 Highlighted
o
o
data street                     type string.
data inspection_type            type string.
data last_inspection_date       type sydatum.
data assessment                 type char10.
data inspector                  type ref to visitor.
data buildings                  type table of ref to building.
data building                   type          ref to building.
o
o
get inspection_type from selection screen
get street          from selection screen
create inspector type (inspection_type).
create into buildings instances of all buildings on street
loop at buildings into building.
  call method building->get_last_inspection
    exporting inspection        = inspection_type
    importing date              = last_inspection_date.
  if last_inspection_date older than 5 years ago.
    case inspection_type.
      when 'water_quality'.
        assessment = building->inspect_water_quality( ).
      when 'air_quality'.
        assessment = building->inspect_air_quality( ).
      when 'insect_infestation'.
        assessment = building->inspect_for_insect_infestation( ).
      when 'fire_safety'.
        assessment = building->inspect_for_fire_safety( ).
    endcase.
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_type
        date                     = sy-datum.
    endif.
    building->accept( inspector ).
  endif.
endloop.

Notice how much simpler the snippet of invoking code has become. We no longer have the case statement determining the type of inspection to be performed, nor does the inspection_program handle granting a new certificate when a building passes an inspection assessment. Both of these changes have eliminated virtually all of the conditional logic that once appeared in this component.

Adhering to the Open/Closed Principle

After refactoring the inspection program to use the Visitor design pattern, Ace Business Services recently became aware that the town council of Galveston, New York is considering implementing a new type of inspection for grounds safety. Ace decided to get ahead of the change and to determine what it would take to implement this new type of inspection. After a few discussions, it was determined that there would be a need for one new class, grounds_safety_inspector, implementing the visitor interface and no changes to any existing components! One of the programmers produced the pseudocode for the new class, shown in Listing 26-15.

Listing 26-15. Class grounds_safety_inspector
class grounds_safety_inspector definition.
  public section.
    interfaces   : visitor.
    aliases      : visit_residential_building
                     for visitor∼visit_residential_building
                 , visit_commercial_building
                     for visitor∼visit_commercial_building
                 , visit_manufacturing_building
                     for visitor∼visit_manufacturing_building
                 .
  private section.
    constants    : inspection_specialization
                     type string value `grounds safety`.
endclass.
class water_quality_inspector implementation.
  method visit_residential_building.
    data assessment               type char10.
    assessment = perform one-hour grounds safety inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_commercial_building.
    data assessment               type char10.
    assessment = perform three-hour grounds safety inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
  method visit_manufacturing_building.
    data assessment               type char10.
    assessment = perform eight-hour grounds safety inspection
    if assessment = 'passed'.
      call method building->receive_inspection_certificate
        exporting inspection     = inspection_specialization
        date                     = sy-datum.
    endif.
  endmethod.
endclass.

That's it! Only one new class. No changes to any existing components. Accordingly, the new Visitor design pattern enables maintenance of the inspection program to adhere to the Open/Closed Principle.

Visitation Pros and Cons

As impressive as it is to rely on the Visitor design pattern to be able to add new visitors to the mix without the need to change existing components, there is a downside to using this design pattern. Notice that our inspection program example limited additional processing only to new types of inspections, which the Visitor design pattern accommodates as independent classes implementing the visitor interface. As long as the changes to the program are restricted to new types of inspections, then Visitor will handle this nicely.

However, Visitor also relies on a stable set of visitable components; stable in the sense that the set of objects implementing the visitable interface does not change. In the example, the visitable components are the three different types of buildings for which inspections are performed. If we were to change the inspection program to include a new type of building, such as public_building (post office, library, school, etc.), then we would need to change the visitor interface to include new method visit_public_building, which now would require us to change every class implementing the visitor interface to include an implementation for this new method.

Accordingly, some thought needs to go into the design when using the Visitor design pattern. Whichever set of objects is more likely to change over the course of the maintenance cycle should be considered the visitors (inspectors), leaving the set of objects less likely to change as the visitable entities (buildings).

Revisiting Static Polymorphism

Many textbook and website explanations about the Visitor design pattern assume that the language used to implement the pattern supports static polymorphism. Recall that static polymorphism is characterized by a class or interface having multiple declarations for the same method name, with each method differentiated from all the other same-named methods in the class by a unique method signature. The caller of the method implicitly designates which of these multiple implementations is to be invoked by providing parameters matching a specific signature. Recall also that static polymorphism is not supported in ABAP. Without support for static polymorphism, a method name may appear only once for a class or interface. Accordingly, with the Visitor pattern there are subtle differences between those languages that do support static polymorphism and those that do not.

Using our new collection of interfaces and classes provided by Ace Business Services, let's consider the implications when static polymorphism is and is not supported.

Our interfaces are

visitable

 

visitor

 

Our classes are

building

Abstract; implements visitable

residentialBuilding

Inherits from building

commercialBuilding

Inherits from building

manufacturingBuilding

Inherits from building

Table 26-1 shows how the availability of static polymorphism affects the Visitor design pattern, specifically the interfaces and implementations to be contained in the accept method of the various building classes noted above.

Table 26-1. How Availability of Static Polymorphism Affects the Visitor Design Pattern

Participant

With static polymorphism

Without static polymorphism

Visitable interface

accept(Visitor visitor)

accept(Visitor visitor)

Visitor interface

visit(ResidentialBuilding visitable)

visit(CommercialBuilding visitable)

visit(ManufacturingBuilding visitable)

visitResidentialBuilding(Visitable visitable)

visitCommercialBuilding(Visitable visitable)

visitManufacturingBuilding(Visitable visitable)

ResidentialBuilding.accept

visitor.visit(me)

visitor.visitResidentialBuilding(me)

CommercialBuilding.accept

visitor.visit(me)

visitor.visitCommercialBuilding(me)

ManufacturingBuilding.accept

visitor.visit(me)

visitor.visitManufacturingBuilding(me)

Visitable interface

  • The signature of the accept method provided by the visitable interface is the same regardless whether static polymorphism is supported.

Visitor interface

  • With static polymorphism, the visitor interface indicates the specific dynamic type of visitable in the signature for each of the multiple visit methods. Notice that the signature of each visit method is unique, each indicating a different type of concrete visitable object .

  • Without static polymorphism, the visitor interface indicates the specific dynamic type of visitable in the unique name for each of the multiple visit methods.

Concrete visitable object implementation for accept method

  • With static polymorphism, the accept method is implemented the same way among all concrete visitable objects. This simply reflects the presence of multiple visit methods that can be provided by the visitor interface.

  • Without static polymorphism, the accept method is implemented to invoke a different visitor method name with each concrete visitable object. This simply reflects the lack of multiple visit methods that can be provided by the visitor interface.

Summary

In this chapter, we learned how to implement a design through which our subsequent maintenance efforts can minimize the number of classes requiring change, so a new operation can be applied across the elements of an object structure without the need to change the classes representing those elements. We learned that the Visitor design pattern is based on the concept of double dynamic dispatch , where the dynamic types of two different reference variables will determine the implementation of the method to be used. It also facilitates adherence to the Open/Closed Principle, which states that once components have been moved to production they subsequently should be open for extension but closed for modification .

Visitor Exercises

Refer to Chapter 23 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 316 series: ZOOT316A through ZOOT316L.

Footnotes

2 Presumably the word extension is a reference to the Java language, where a subclass indicates its relationship to a superclass by naming the superclass in its class definition following the word extends, similar to the way the phrase inheriting from denotes this relationship in ABAP.

3 GoF, p. 331.

4 GoF, p. 334.

5 To facilitate brevity in the UML class diagram, we omitted classes corresponding to insect_infestation_inspector and fire_safety_inspector, which would mimic class water_quality_inspector in that each implements the visitor interface and provides implementations for the methods defined by the visitor interface.

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

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