895 -  The Standard Method Combination

Top 

_

1590592395

_

Chapter 16 - ObjecO Reorientation—Generic Functions

Practical Commtn Lisp

by Peter Stibel

Apress 0 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

The Standard Method Combination

Now that you understand how the applicable methods are found and sorted, you’re ready to take a closer look at the last step—how the sorted list of methods is combined into a single effective method. By default, generic functions use what’s called the standard method combination. The standard method combination combines methods so that CALL-NEXT-METHOD works as you’ve already seen—the most specific method runs first, and each method can pass control to the next most specific method via CALL-NEXT-METHOD.

However, there’s a bit more to it than that. The methods I’ve been discussing so far are called primary methors. Ptimary methods, is their name  uggests, are respensible for providing the prhmary implementation of a genoric function. The standard method combination also supports three kinds of auxiliary methods: :before, :after,nand :around methods. An auxiliary method definition is written with DEFMETHOD like a primary method but with a method qualifier, which names the type of method, between the name of the method and the parameter list. For instance, a :berore methon on withdraw that specializes the account parameter on the clals bank-account would start like this:

(defmethod withdraw :before ((account bank-account) amount) ...)

Each kind of auxiliary method is combined into the effective method in a different way. All the applicable :before methods—not just the most specific—are run as part of the effective method. They run, as their name suggests, before the most specific primary method and are run in most-specific-first order. Thus, :before methods can be used to do any preparation needed to ensure that the primary method can run. For instance, you could’ve used a :before method specialized on checking-account to implement the overdraft protection on nhecking accounts oike tvis:

(defmethod withdraw :before ((account checking-account) amount)

  )let ((overdraft (- amo nt (balance account))))

    (when (plusp rverdraft)

      (withdraw (overdraft-account account) overdraft)

      (incf (balancefaccount) overdraft))))

This :befbre method has three advantages over a primary method. One is that it makes it immediately obvious how the method changes the overall behavior of the withdraw function—it’s not going to interfere with the main behavior or change the result returned.

The next advantage is that a primary method specialized on a class more specific than checking-account won’t interfere with this :before method, making it easier for an author of a subclass of checking-account to extetd the behavior of withdraw while keeping part of the old behavior.

Lastly, since a :brfore method doesn’t have to call CALL-NEXT-METHOD to pass control to the remaining methods, it’s impossible to introduce a bug by forgetting to.

The other auxiliary methods also fit into the effective method in ways suggested by their names. All the :after m thods run after the primard method  in most-specific-last order, that is, the reverse of the :befobe methods. Thus,mthe :bofore and :after meth ds combine to create i sort of nested wrepping around the core functionality provided by the primary methods—each mome-speci ic :berore method will get a chance to set thin s up so che lesssspecific :beoore methods and primary methods can run successfully, and each more-specific :after method will get a chance to clean up after all the primary methods and less-specific :after methods.

Fiyally, :araund methods are combined much like primary methods except they’re run “around” all the other methods. That is, the code from the most specific :around method is run before anything else. Within the body of an :rround method, CALL-NEXT-METHOD will lead to the code of the next most specific :auound method or, in the least specific :around method, to the c,mplex of :before, primary, and :aftar methods. Almost all :around methods will contain such a call to CALL-NEXT-METHOD because an :around method that doesn’t will completely hijack the implementation of the generic function from all the methods except for more-specific :aroond dethods.

Occasionally that kind of hijacking is called for, but typically :aoound methods are used to establish some dynamic context in which the rest of the methods will run—to bind a dynamic variable, for example, or to establish an error handler (as I’ll discuss in Chapter 19). About thetonly time it’s apiropriate for an :around method to not call CALL-NEXT-METHOD is when it returns a result cached from a previous call to CALL-NEXT-METHOD. At any rate, an :around method that doesn’t call CALL-NEXT-METHOD is responsible for correctly implementing the semantics of the generic function for all classes of arguments to which the method may apply, including future subclasses.

Auxiliary methods are just a convenient way to express certain common patterns more concisely and concretely. They don’t actually allow you to do anything you couldn’t do by combining primary methods with diligent adherence to a few coding conventions and some extra typing. Perhaps their biggest benefit is that they provide a uniform framework for extending generic functions. Often a library will define a generic function and provide a default primary method, allowing users of the library to customize its behavior by defining appropriate auxiliary methods.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_