Callbacks, Observers, Listeners, and Notifiers

Pretty much any system that requires a low-latency response to asynchronous events uses some form of callbacks, observers, or notifiers. They can be found in user interface toolkits, in which they are often called event listeners, and distributed systems. Callbacks are a generic mechanism allowing you to supply code that the system invokes when certain well-known events happen. They have a variety of forms, some making creative use of language-specific features. Languages like C, Perl, JavaScript, and others with a procedural or functional flavor use function pointers (Listing 12-6) or function references (Listing 12-7). Object-oriented languages often implement callbacks through implementations of special-purpose interfaces as in Listing 12-8. Languages with reflection or dynamic invocation capabilities can provide callback functionality through strings representing the names of classes, methods, or functions; Listing 12-9 shows an example using the Spring Scheduling framework. In some cases, operating systems present language-independent reflection-like mechanisms such as the POSIX dynamic-loading APIs (Listing 12-10). C++ pointer-to-member-function syntax (Listing 12-11) supports a kind of type-constrained callback. C++ function invocation operator overloading allows the use of function objects, or functors,6 as a form of callback as shown in Listing 12-12.

6. http://en.wikipedia.org/wiki/Function_object

Listing 12-6: Example from the Standard C Library7 of an interface requiring a function pointer

#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
    int (*compar)(const void *, const void *));

7. ISO/IEC 9899:1990 Section 7.10.5.2

Listing 12-7: Callback functions are commonplace in JavaScript, such as in the use of jQuery.each() shown here.

$('.menu > .menuitem').each(function(index, element) {
  console.log('Item ' + index + ' says ' + $(element).text());
});

Listing 12-8: Java’s Swing user interface framework defines a large number of listener interfaces, such as the ActionListener used here for a simple button response.

public class HiThere implements ActionListener {
  ...
  public void init() {
    button.addActionListener(this);
  }
  ...
  public void actionPerformed(ActionEvent e) {
    textArea.append("Hello, World!");
  }
}

Listing 12-9: Spring Scheduling configuration for a reflection-based scheduling callback defined by strings detailing the class and method to be invoked. Note that “Image” indicates an artificial line break for purposes of formatting.

<bean id="runMeJob"
    class="org.springframework.scheduling.quartzImage
      .MethodInvokingJobDetailFactoryBean">
  <property name="targetObject" ref="runMeTask" />
  <property name="targetMethod" value="printMe" />
</bean>

Listing 12-10: Example of using the POSIX dlopen(3) API8 to load and invoke a function from a shared library as a callback.

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
/* For NAN macro */
#define _GNU_SOURCE
#include <math.h>

double
call_double_return_double(char *libname,
    char *funcname, double arg)
{
  void *handle;
  double (*func)(double);
  char *error;
  double result;

  handle = dlopen(libname, RTLD_LAZY);
  if (NULL == handle) {
    fprintf(stderr, "%s ", dlerror());
    return NAN;
  }

  dlerror();    /* Clear any existing error */

  *(void **) (&func) = dlsym(handle, funcname);

  error = dlerror();
  if (error != NULL)  {
    fprintf(stderr, "%s ", error);
    return NAN;
  }

  result = (*func)(arg));
  dlclose(handle);
  return result;
}

8. http://linux.die.net/man/3/dlopen

Listing 12-11: Using C++ pointer-to-member functions to implement a callback with a signature like that in Listing 12-11

typedef double (Callback::*CallbackMember)(double);

call_double_returns_double(Callback &cb,
    CallbackMember cbm, double arg)
{
  return cb.*cbm(arg);
}

Listing 12-12: Example of the base class for a basic C++ functor that takes a string as its argument and returns nothing. Using templates and pointers to member functions, you can create some very sophisticated generalizations of functors in C++. The Boost library supports creation of functors with combinations of boost::function and boost::bind, which were incorporated into the C++11 standard’s <functional> header.9

class AbstractFunctor
{
public:
  virtual void operator()(const char *string)= 0;
}

9. ISO/IEC 14882:2011 Section 20.8

Specific uses of callbacks have become known by more specific names representing their particular purposes. The Observer pattern [DP] uses callbacks in a specialized way to immediately communicate when a particular state or condition changes. Another specialized form of callback, the notifier, allows client code to register interest in specific events, often as part of a logging or monitoring strategy.

All callbacks have the common purpose of synchronously communicating various conditions to interested code. Often the conditions that are the subject of the communication are otherwise encapsulated. For testing purposes, you should even consider the invocation of a callback as a feature requiring verification.

However, this feature of the code’s behavior also represents an exploitable seam. Callbacks usually accept data about the change or event that occurred. Callbacks also occur at well-understood times in the processing of the system that makes them available. More often than not, callbacks can be used to verify that the system handles and passes on data correctly, even when no other direct means is available. You can use mock implementations of callbacks for behavioral verification. You can even use them to exert control over test and thread execution, as we will see in Chapter 13.

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

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