Notes from lecture 5 – The proxy pattern

Notes from lecture 5 – The proxy pattern

The two design patterns we discuss in lecture 4 belong to the so-called behavioral group. Such patterns implement communication protocols between a collection of objects, e.g., the visitor objects and the shape objects in the visitor pattern.

Besides behavioral patterns, other groups of patterns focus on
  • how to create new objects or collections of new objects that meet certain constraints (creational patterns);

  • how to manage objects in a multi-threading world (concurrency patterns); and

  • how to organize objects in structures that realize relations between objects (structural patterns).

The proxy pattern is a very powerful structural pattern that solves the problem of modifying the behavior of an object without changing the implementation of the class of the object nor the way other code interacts with the object.

To put this in concrete terms, consider the implementation for a board game player that receives some tokens as the game is played and has to track them over time.
(define player<%>
  (interface () register receive-token make-a-move))
(define player%
  (class* object% (player<%>)
    (super-new)
    (init-field name)
    (define my-game-state '())
    (define/public (register)
      name)
    (define/public (receive-token token)
      (set! my-game-state (cons token my-game-state)))
    (define/public (make-a-move board)
      board)))
If we want to add contract checking for the player%’s methods the most straightforward option is to modify the definition of player% to include assertions in its methods’ code. Besides the fact that this approach mixes validation/checking code and functionality code and thus makes it hard to test and evolve those independently, it is also not scalable. In particular, if for debugging we want to add some logging mechanism we have to inject more code to the implementation and comment it out carefully when we deploy the code. Worse, we cannot enable the contract code or the logging code selectively for the objects or part of our system we need; it is part of the methods of every player% object.

The proxy pattern offers an alternative that is scalable, composable and requires no changes to the player% class and minimum changes to the code that uses the player% object. The trick is quite simple: we define another class that implements the player<%> interface, has a field that stores an object of a class that implements player<%>, and methods that after some processing invoke the corresponding methods of the stored object and process the result of these invocations before returning themselves.

Here is how the above plays out for creating a proxy that does contract checking:
(define player-contract-proxy%
  (class* object% (player<%>)
    (super-new)
    (init-field the-real-player)
    (define/public (register)
      (define name (send the-real-player register))
      (if (string? name)
          name
          "something is wrong with the real player"))
    (define/public (receive-token token)
      (if (string? token)
          (send the-real-player receive-token token)
          "something is wrong with the game admin"))
    (define/public (make-a-move board)
      (send the-real-player make-a-move board))))
The methods of player-contract-proxy% intercept arguments that are intended for the methods of a player% object, validate them, forward them, and perform the same for the results:
> (define christos (new player% [name 'christos]))
> (define christos-with-contract (new  player-contract-proxy% [the-real-player christos]))
> (send christos-with-contract register)

"something is wrong with the real player"

> (send christos-with-contract receive-token 'VoidWarden)

"something is wrong with the game admin"

The same way as for contract checking we can create a new proxy for logging:
(define player-logging-proxy%
  (class* object% (player<%>)
    (super-new)
    (init-field the-real-player)
    (define/public (register)
      (displayln "register called")
      (send the-real-player register))
    (define/public (receive-token token)
      (displayln "receive-taken called")
      (send the-real-player receive-token token))
    (define/public (make-a-move board)
      (displayln "make-a-move called")
      (send the-real-player make-a-move board))))
Notice that we can either compose the new proxy with the contract checking one:
> (define christos-with-contract+logging
    (new  player-logging-proxy% [the-real-player christos-with-contract]))
> (send christos-with-contract+logging register)

register called

"something is wrong with the real player"

or we can use it by itself:
> (define christos-with-contract+logging
    (new  player-logging-proxy% [the-real-player christos]))
> (send  christos-with-contract+logging register)

register called

'christos

The following class diagram depicts the static aspects of the relationships between objects as captured by the proxy pattern in our running example:

Specifically, both the proxy and the actual player% class implement the player<%>. Moreover, the player proxy encapsulates an instance of the player% class. As we have seen above with the logging proxy, the latter can be generalized so that the proxy encapsulates an instance of any class that implements the player<%> interface. One important bit of information from the diagram is that code that interacts with player% objects can keep doing so even if they are proxied as both player% objects and their proxies implement the same interface. This diagram gives an alternative view of the proxy pattern:

Instead of the static relationships between the objects involved, this so-called sequence diagram shows how they interact at runtime. Vertical lines correspond to the lifetimes of objects, horizontal lines from left-to-right to method calls, horizontal dashed lines from right-to-left to method returns, and white rectangles to method body computation. In a succinct manner the diagram communicates that the proxy object intercepts all method invocations and returns of the player%