Notes from Lecture 4 – Design patterns II
1 The proxy pattern
2 The power of proxies:   remote objects

Notes from Lecture 4 – Design patterns II

The two design patterns we discuss in lecture 3 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 patters 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).

1 The proxy pattern

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 Santorini player:
(define player<%>
  (interface () register place play))
(define player%
  (class* object% (player<%>)
    (super-new)
    (init-field name)
    (define my-workers '())
    (define/public (register)
      name)
    (define/public (place color)
      (set!
       my-workers
       (list (string-append color 1)
             (string-append color 2)))
      '((0 0) (0 4)))
    (define/public (play board)
      (list (first my-workers) '("N","N")))))
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)
      (let ([name
             (send the-real-player register)])
        (if (string? name)
            name
            "something is wrong with the real player")))
    (define/public (place color)
      (if (string? color)
          (send the-real-player place color)
          "something is wrong with the referee"))
    (define/public (play board)
      (send the-real-player 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 place 'blue)

"something is wrong with the referee"

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 (place color)
      (displayln "place called")
      (send the-real-player player color))
    (define/public (play board)
      (displayln "play called")
      (send the-real-player board))))
Notice that we can both 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

Figure 1 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.

Figure 1: A static view of the proxy pattern

Figure 2 gives an alternative view of the proxy pattern. Instead of the static relationships between the objects involved, it 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. The diagram communicates that the proxy object intercepts all method invocations and returns of the player%.

Figure 2: A dynamic view of the proxy pattern

2 The power of proxies: remote objects

The diagram in figure 2 is a so-called sequence diagram where all communication between objects happens with method invocations. The proxy pattern though is so powerful that it can be useful in situations that this fundamental assumption does not hold.

For example, consider the case of a remote player for Santorini. The remote player is by nature a network client to the Santorini server/admin. Thus the admin needs to communicate with it with network messages over TCP/IP or some other protocol rather than method calls. This seems to imply that to support remote players we have to change completely the architecture of the game to recognize if a player is local or not and pick the right mode of communication (methods VS network messages).

The solution that the proxy pattern offers is to hide the network player behind a proxy. This way the admin “thinks” it interacts with another player but in reality the proxy translates method invocations to network messages. Dually, at the player’s end we can add a driver component, similar to the test harnesses for unit testing, that receives the network messages and translates them back to method invocations for the actual player object. Figure 3 depicts the adapted design.

Figure 3: The remote proxy pattern