The proxy pattern abstracts the true server from the client by means of a stand-in or surrogate class, providing a separation of a client and a server, and allowing specified properties of the server to be hidden from the clients. 9.4.1 AbstractThe proxy pattern decouples the true server from the client by means of a stand-in or surrogate class. There are a number of reasons why this may be useful, such as to hide some particular implementation properties from the clients and thus allow them to vary transparently to the client. For our purposes, the primary reason to use the proxy pattern is to hide from its client the fact that a server may be actually located in another address space. This allows the server to be located in any accessible location and the clients need not concern themselves with how to contact the true server to access required information or services. 9.4.2 ProblemThe design of modern embedded systems must often be deployed across multiple address spaces, such as different processors. Many times such details are subject to change during the design process, or even worse, during the implementation of the system. It is difficult to hard-code the knowledge that a server may be remote because this may change many times as the design progresses. Further, the clients and servers may be redeployed in other physical architectures, using different communications media, and if the clients are intimately aware of these design details then porting the clients to the new platforms is made more difficult. The two primary problems addressed by the proxy pattern are the transparency of the potential remoteness of the servers and the hiding and encapsulation of the means by which to contact such remote servers. 9.4.3 Pattern StructureThe proxy pattern, shown in Figure 9-3, clearly gets its lineage from the observer pattern. Indeed, the proxy pattern differs primarily in that it adds a proxy between the Abstract Client and the Abstract Subject classes. Figure 9-3. Proxy PatternThe pattern has two sides. In the first side, the Client-side Proxies subscribe to the Server-side Proxies, which publish the data under the command of the Concrete Servers. When the Concrete Servers call the send() operation, all the remote Client-side Proxies are notified of the new data. On the other side, the Concrete Clients subscribe in turn to the Client-side Proxies, just as in the observer pattern the Concrete Clients subscribe to Concrete Servers. When these Client-side Proxies are notified of new data, they walk their notification lists to send the data to all their local subscribers. Although the structure of the pattern emphasizes the exchange of Data objects, this is only one kind of service that can be performed via the proxy pattern. In fact, any service may be published by the server and accessed via the proxy classes, even if no data is actually exchanged. 9.4.4 Collaboration Roles
9.4.5 ConsequencesThe proxy pattern does a good job of isolating the subject from knowledge that the server may be remote. The advantage to this is that the clients are simplified, not having to deal differently with remote and local clients. The proxy pattern also encapsulates the knowledge of how to contact the servers into the proxy classes so that should the communications media change, fewer classes must be updated. Because there are usually many fewer client proxy instances (one per data type per address space) than client instances, the traffic on the communications media is minimized. One message is sent across the bus or network for each proxy, rather than one per client. This reduces bus traffic, a common bottleneck in embedded and real-time systems. Bus traffic is reduced even further because of the use of a subscription policy, resulting in transmission of the data only when necessary, as opposed to polling for the data. 9.4.6 Implementation StrategiesOn the local (client-proxy) side, the same implementation strategies used for the observer pattern apply here. The AbstractClient objects subscribe to the Client-side Proxies in the same way as in the observer pattern. For the remote (server) side, the implementation is generally highly protocol-specific. The Server-side Proxy marshals the messages from the AbstractServer and invokes the communications system to transmit them to its clients, the remote Client-side Proxy objects. The Server-side Proxy can do this because the Client-side Proxy objects subscribe to the Server-side Proxy. Note that in this case, the Client-side Proxy objects must know a priori how to contact the Server-side Proxy for the desired information. Thus, this pattern is especially useful on asymmetric distribution architectures, that is, architectures in which the locations of objects are known at design time. Note also that both the Client-side Proxy and the Server-side Proxy classes aggregate Notification Handle objects via composition. This latter class will typically be subclassed into "local" and "remote" flavors as an optimization. LocalNotificationHandles may be simple function pointers to the accept() method of the ConcreteClient class. Remote NotificationHandles must rely on the underlying transport protocol for message delivery. 9.4.7 Sample ModelThe example for the proxy pattern is shown in Figures 9-4 and 9-5. The first figure shows the structure of the collaboration and the mapping of the objects onto the physical architecture. The latter figure gives an example of how such a system behaves. Figure 9-4. Proxy Example StructureFigure 9-5. Proxy Example ScenarioFigure 9-4 shows four nodes: the GasMixer, Safety, MedicalDelivery, and UserControl processors. The GasMixer contains the server, an object of class O2FlowSensor. This connects with a server-side proxy class O2 FlowServerProxy. This proxy aggregates Object IDs to use as addresses on the bus connecting the nodes running a custom communications protocol. This bus (shown as the heavy lines connecting the nodes) provides the physical means to deliver the messages among the objects running on different processors. The GasMixerProcessor contains the O2FlowSensor, which acts as a server for the data. The O2FlowSensor invokes O2ServerProxy::send() to send the data to all the registered clients. This is done by walking the notification handle list (which holds Object IDs that the lower-level communications protocol uses for message delivery) and sending a message to each registered client (the O2FlowClientProxies). There are four clients of the Flow data object: the SafetyMonitor running on the SafetyProcessor, the InspirationController and the Vaporizer running on the MedicalDeliveryProcessor, and the HistogramView running on the UserControlProcessor. Each processor containing at least one client also has a single O2FlowClientProxy instance to obtain the value from the O2FlowServerProxy. The scenario in Figure 9-5 shows the clients subscribing to their client proxies and the client proxies subscribing to the server proxy. Later, when the O2FlowSensor receives an update, it invokes the O2FlowServerProxy::send() operation, which walks the NotificationHandle list (not shown to save space) and for each registered client proxy sends the data. In turn, the receiving client proxy walks its client list, for the ultimate delivery of the data. Note: Although the send() operation walks the list of subscribers (RemoteNotificationHandles) in a serial fashion, the delivery of the messages is generally asynchronous, and you cannot determine the arrival order of the messages from the sending order. This is because the objects in different addresses usually operate in different threads, so relative order cannot be determined. That is why in the second update, a different deliver order is shown with respect to the delivery of the data to the concrete clients. |