© James E. McDonough 2017

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

20. Command 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 Command design pattern, another of the design patterns found in the GoF catalog. We will find this design pattern useful when we want to trigger the execution of an activity without having to know what the activity is.

The Command design pattern is most applicable in situations where the requester for an activity is to remain oblivious to the implementation details for completing the activity, and contributes to enabling objects to remain only loosely coupled .

Plumbing Business Scenario

Acme Plumbing and Heating has 50 plumbing, heating, ventilation, and air conditioning technicians who service the plumbing and heating needs of the residents of Ottawa, Ontario. Although Acme often is awarded large jobs, such as installing the plumbing infrastructure into a new office building under construction, which can take many weeks, it primarily offers scheduled and emergency plumbing services to city residents. Calls for services are handled through a service call center, with each call for service resulting in the creation of a service work order.

These technicians average about three stops per day per technician throughout the week, with their daily schedules arranged by a central dispatching office and consisting of a queue of service work orders to be completed, arranged in a sequence based on the priority of the work. Of course, this only represents the expected work for the day, as emergency calls can be assigned to the technician throughout the day, and these emergency service work orders take priority over the scheduled stops.

The work schedules are available to each technician via an electronic list, available either by an on-board computer in the truck or by a mobile phone application. Upon arriving at work each day, each technician pulls the first work order from their queue. The work order includes the address where the work is to be performed and a description of the work to be completed. Although the service call center operators and technician dispatchers have some working knowledge of plumbing and heating considerations, enough to be able to arrange the priorities for the scheduled jobs and to communicate what is to be done, they do not include detailed information for how to do the job, the details of which are left up to the technician assigned the work order.

The following are some of the more common activities included in work orders assigned to the Acme technicians:

  • Fix leaky faucet .

  • Clear clogged drain.

  • Replace broken pipe.

  • Bleed all radiators.

  • Inspect furnace.

  • Install water heater.

The following is a typical winter scenario. The technician working the northwest section of the city has a list of four work orders in the queue, and is already nearly complete with the first one. Meanwhile, a resident in that area who finds one of the pipes in the house has frozen and burst open places a call to Acme for emergency service. The service call center agent creates a work order for the technician who works that section of the city. When that technician notifies the dispatcher that the current job has been completed, the dispatcher issues the next work order assigned to that technician, which now happens to be the emergency call to fix a burst pipe. The technician attends to this emergency as the next task, deferring the other regularly scheduled work orders. Upon completing the emergency call, the technician continues with the next work order in the queue.

A service work order represents a command for the service technician to perform some task. The service call center creates these work orders and the dispatcher assigns them in a sequence to make the most efficient use of the service technician’s time. Other than an estimate for how much time a work order should take to complete, the dispatcher does not require any details about the service activities that make up the work order. Accordingly, the dispatcher merely commands the service technician to do some work at a specific time, but does not need to know what that work entails.

This is the essence of the Command design pattern: the requester entity (dispatcher) who requests a provider entity (service technician) to perform some task (work order) may have no knowledge of the action to be performed by the provider entity. It enables a caller to trigger an activity without knowing any details about the activity itself.

A Command Performance

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

  • Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. 1

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

  1. Command: Declares an interface for executing an operation.

  2. ConcreteCommand: Defines a binding between a Receiver object and an action. Implements the Execute method by invoking the corresponding operation(s) on the Receiver.

  3. Client: Creates a ConcreteCommand object and sets its receiver.

  4. Invoker : Asks the command to carry out the request.

  5. Receiver: Knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver.

The UML class diagram for the Command design pattern is shown in Figure 20-1.

A447555_1_En_20_Fig1_HTML.jpg
Figure 20-1. UML class diagram for the Command design pattern

Notice that the Invoker participant merely calls the execute method of its Command participant. It is unaware of the action the Command participant will cause to be performed. Notice also that the ConcreteCommand participant invokes the action method upon the receiver instance with which it is constructed. In addition, notice that it is the Client participant that creates the instance of a ConcreteCommand participant.

Figure 20-2 shows the UML class diagram for the Command design pattern as it would apply to Acme Plumbing and Heating.

A447555_1_En_20_Fig2_HTML.jpg
Figure 20-2. UML class diagram for the Command design pattern applied to plumbing services scenario

We see that the Acme Service Call Center object creates a service work order for a specific service technician based upon receiving a service call to fix a leaky faucet. The Acme dispatcher requests of the Acme Service Call Center object the next work order assigned to that technician, and then commands that technician to do some work, without having to know any of the details of the work to be performed, the details of which are encapsulated into the service work order assigned to the service technician.

The Acme dispatcher always invokes the same behavior on the work order: the simple command to do work. It is the service work order that contains the details to convert this command into the actual behavior to be invoked upon the service technician object: fix a leaky faucet.

Command in ABAP

The following code shows how we might implement the Command design pattern UML class diagram illustrated above into functioning ABAP code. The service_technician class , shown in Listing 20-1, plays the role of the Receiver participant and is shown first because it depends on none of the other participants in this Command design.

Listing 20-1. Class service_technician
class service_technician definition.
  public section.
    methods      : go_to_worksite
                     importing worksite type string
                 , fix_leaky_faucet
                 , clear_clogged_drain
                 , replace_broken_pipe
                 , bleed_radiators
                 , inspect_furnace
                 , install_water_header
                 .
endclass.
class service_technician implementation.
  method go_to_worksite.
    travel to specified worksite
  endmethod.
  method fix_leaky_faucet.
    fix leaky faucet
  endmethod.
  method clear_clogged_drain.
    clear clogged drain
  endmethod.
  method replace_broken_pipe.
    replace broken pipe
  endmethod.
  method bleed_radiators.
    bleed radiators
  endmethod.
  method inspect_furnace.
    inspect furnace
  endmethod.
  method install_water_header.
    install water heater
  endmethod.
endclass.

Notice that the service_technician class has a method to instruct the technician where to go to provide the service and other methods to tell the technician what to do upon arriving.

Next is the work_order abstract class, shown in Listing 20-2, playing the role of the Command participant; it simply provides the abstract public method do_work.

Listing 20-2. Abstract class work_order
class work_order definition abstract.
  public section.
    methods      : do_work abstract.
endclass.

Class service_work_order, shown in Listing 20-3, inherits from the work_order class shown above and plays the role of the ConcreteCommand participant.

Listing 20-3. Class service_work_order
class service_work_order definition inheriting from work_order.
  public section.
    methods      : constructor
                     importing technician type ref to service_technician
                               problem    type string
                               worksite   type string
                 .
  private section.
    data         : technician type ref to service_technician
                 , problem    type string
                 , worksite   type string
                 .
endclass.
class service_work_order implementation.
  method constructor.
    call method super->constructor.
    me->technician                = technician.
    me->problem                   = problem.
    me->worksite                  = worksite.
  endmethod.
  method do_work.
    call method technician->go_to_worksite
      exporting worksite = me->worksite.
    case me->problem.
      when 'leaky faucet'.
        call method me->technician->fix_leaky_faucet.
      when 'clogged drain'.
        call method me->technician->clear_clogged_drain.
      when 'broken pipe'.
        call method me->technician->replace_broken_pipe.
      when 'radiators'.
        call method me->technician->bleed_radiators.
      when 'furnace'.
        call method me->technician->inspect_furnace.
      when 'water header'.
        call method me->technician->install_water_heater.
    endcase.
  endmethod.
endclass.

Notice in Listing 20-3 the definition of a constructor for the service_work_order class, by which the instance retains information about its technician instance, the problem to be fixed, and the location of the work site. Its implementation of the do_work method first sends the technician to the work site and then issues the command associated with the problem to the technician.

Listing 20-4 shows the definition of class acme_service_call_center, playing the role of the Client participant .

Listing 20-4. Class acme_service_call_center
class acme_service_call_center definition.
  public section.
    constants    : priority_emergency type char1 value '1'
                 , priority_high      type char1 value '2'
                 , priority_medium    type char1 value '3'
                 , priority_low       type char1 value '4'
                 .
    methods      : constructor
                 , create_work_order
                     importing requested_technician type string
                               problem              type string
                               worksite             type string
                 , get_next_work_order
                     importing requested_technician type string
                     exporting work_order type ref to work_order
                 .
  private section.
    types        : begin of technician_entry
                 ,   technician_name type string
                 ,   technician_instance type ref
                                  to service_technician
                 , end   of technician_entry
                 , begin of work_order_entry
                 ,   priority        type char1
                 ,   technician_name type string
                 ,   work_order type ref
                                  to work_order
                 , end   of work_order_entry
                 .
    data         : technician_stack type standard table
                                      of technician_entry
                 , work_order_stack type sorted table
                                      of ref to work_order
                                      with non-unique key priority
                 .
endclass.
class acme_service_call_center implementation.
  method constructor.
    data         : technician_entry like line of me->technician_stack.
    technician_entry-name = 'Smith'.
    create object technician_entry-technician_instance.
    append technician_entry to me->technician_stack.
    technician_entry-name = 'Jones'.
    create object technician_entry-technician_instance.
    append technician_entry to me->technician_stack.
      o
      o
  endmethod .
  method create_work_order.
    data         : technician_entry like line of me->technician_stack
                 , work_order_entry like line of me->work_order_stack
                 .
    read table me->technician_stack
          into technician_entry
          with key technician_name = requested_technician.
    work_order_entry-technician_name = technician_entry-technician_name.
    work_order_entry-priority = priority.
    create object work_order_entry-work_order
             type service_work_order
      exporting technician = technician_entry-technician_instance
                problem    = problem
                worksite   = worksite.
    append work_order_entry to me->work_order_stack.
  endmethod.
  method get_next_work_order.
    data         : work_order_entry like line of me->work_order_stack
                 .
    read table me->work_order_stack into work_order
      with key technician_name = requested_technician.
    delete table me->work_order_stack index sy-tabix.
  endmethod.
endclass.

The constructor method shown in Listing 20-4 builds the list of technicians into the internal table technician_stack. The create_work_order method facilitates placing a new work order into the internal table work_order_stack after finding from table technician_stack the technician instance for the requested technician. The get_next_work_order method retrieves from the internal table work_order_stack the next entry associated with the requested technician and then deletes this entry from the table.

Finally, the acme_dispatcher class, shown in Listing 20-5, plays the role of the Invoker participant.

Listing 20-5. Class acme_dispatcher
class acme_dispatcher definition.
  public section.
    methods      : constructor
                     importing service_call_center
                       type ref to acme_service_call_center
                 , dispatch_next_work_order
                     importing technician_name type string
                 .
  private section.
    data         : service_call_center type ref to acme_service_call_center
                 , next_work_order     type ref to work_order
                 .
endclass.
class acme_dispatcher implementation.
  method constructor.
    me->service_call_center       = service_call_center.
  endmethod.
  method dispatch_next_work_order.
    call method me->service_call_center->get_next_work_order
      exporting requested_technician = technician_name
      importing work_order           = me->next_work_order.
    call me->next_work_order->do_work.
  endmethod.
endclass.

Notice in Listing 20-5 that the acme_dispatcher class has a constructor method accepting a reference to an instance of an acme_service_call_center. It needs a reference to this service call center so it can invoke its method to get the next work order. Notice the implementation for method dispatch_next_work_order, which calls upon the service call center to provide it the next work order for the technician and then simply invokes the do_work method on that work order instance. The dispatcher instance is oblivious to the service to be performed by the service technician it is dispatching; all the information regarding the location of the work site and the work to be done is contained within the work order.

An entity to make use of these participants might contain code that looks similar to Listing 20-6.

Listing 20-6. Entity Making Use of Participants Defined Above
o
o
data           : service_call_center
                   type ref to acme_service_call_center
               , service_dispatcher
                   type ref to acme_dispatcher
               .
o
o
" 1
create object service_call_center.
" 2
create object service_dispatcher
  exporting service_call_center = service_call_center.
o
o
" 3
call method service_call_center->create_work_order
  exporting requested_technician = `Jones`
            problem              = `leaky faucet`
            priority             = acme_service_call_center=>priority_low
            worksite             = `105 Sycamore Street`.
o
o
" 4
call method service_call_center->create_work_order
  exporting requested_technician = `Smith`
            problem              = `broken pipe`
            priority             = acme_service_call_center=>priority_high
            worksite             = `2296 Ontario Blvd.`.
o
o
" 5
call method service_dispatcher->dispatch_next_work_order
  exporting technician_name = `Smith`.
call method service_dispatcher->dispatch_next_work_order
  exporting technician_name = `Jones`.
o
o

In the fragment of code in Listing 20-6 we see the following:

  1. An instance of class acme_service_call_center is created.

  2. An instance of class acme_dispatcher is created.

  3. The instance of class acme_service_call_center is used to create a service work order for technician Jones to fix a leaky faucet.

  4. The instance of class acme_service_call_center is used to create a service work order for technician Smith to fix a broken pipe.

  5. The instance of acme_dispatcher is used to dispatch the next work order to technician Smith and the following work order to technician Jones.

Dependencies and Dependency Injection

Refer back to the private section defined for class acme_dispatcher shown in Listing 20-5. Notice it defines two attributes: one is a reference to an instance of class acme_service_call_center and the other is a reference to an instance of class work_order. Anytime an entity defines a storage field as a reference to an instance of some other class, the entity becomes dependent upon that type of class; indeed, it is coupled to that class. Accordingly, class acme_dispatcher is dependent upon classes acme_service_call_center and work_order. It means, among other things, that when both classes are defined as local classes, as illustrated in the code above, the definition of the dependent class must follow that of the class upon which it is dependent. It also means that code changes made to the class upon which it is dependent can have syntax or execution ramifications upon the dependent class.

Now refer back to the public section defined for class acme_dispatcher shown in Listing 20-5. Notice it defines a constructor method accepting a reference to an instance of a class. Anytime a method signature indicates that it accepts a reference to an instance of a class, dependency injection is at work. Dependency injection means that the caller is providing a reference to an instance of a class for the called method to use. The caller essentially injects the instance to be used into the called method through its signature.

Compare dependency injection with the scenario where a class or its methods facilitates creating its own instances of classes to be used, or requests other objects to provide it with a reference to an instance of a class. Neither constitutes injection since the class or method is actively requesting a reference to an instance. It is only when a method signature accepts a reference to an instance of a class that injection occurs.

Summary

In this chapter, we learned how to define a set of classes where the class initiating the request for some activity to be executed knows nothing about the process associated with completing that activity. The Command design pattern promotes flexible designs, including the instantiation of classes that can facilitate reversible operations and reduces the information classes need to know about each other.

Command Exercises

Refer to Chapter 17 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 310 series: ZOOT310A through ZOOT31C.

Footnotes

1 GoF, p. 233.

2 GoF, p. 236.

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

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