Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 41
Текст из файла (страница 41)
However, as discussed inChapter 4, this would quickly drain the device’s battery. Instead, anCOORDINATOR213event-driven programming approach is needed that is able to respectthe existing dependencies between components by notifying them in astructured manner.ExampleConsider the start up of a device. Instead of the tens of servers andapplications started on a real device, we limit ourselves in this exampleto the following components which start in the following order:1.
The system starter starts the system, and hence the other threecomponents, with the goal of making the device appear to start up asquickly as possible.2. The Communications Infrastructure creates and initializes all thecommunication protocols on the device.3.
The phone application allows the user to make phone calls.4. The task scheduling server schedules tasks to be run at specific times.For the device we’re using as an example here, only half the communication protocols are actually required by the phone application whilstthe scheduling server is deemed to be a low priority and hence doesn’tneed to start running scheduled tasks before the device appears to finishstarting up. However, this might well be different on another device so thedevice start-up architecture needs to be flexible enough to accommodatethis.However, for this specific device one way to optimize device start-up23is by changing the device start-up sequence to be as follows:1.
The System starter starts . . .2. The Communications Infrastructure phase 1 which loads just theprotocols needed by the phone application and then starts . . .3. The Phone application. The device now appears to have booted butthe lower priority functionality should be started in the backgroundso this application calls . . .4. An API introduced into the Communications Infrastructure, RSocketServ::LoadAllProtocols() which starts . .
.5. The task scheduling server.This solution has the problem that the responsibility for starting thesystem is now split between a number of components which adds23 The user’s perception of the phone having been started is when they can start makingphone calls using the phone user interface, so to optimize device start-up, the phone userinterfaces should be initialized and working as the number one priority with all othercomponents being started afterwards in the background.214PROVIDING SERVICEScomplexity to an already complex area of functionality. The supply chainis made more intricate too since the phone application is provided bythe device manufacturer whilst the Communications Infrastructure isprovided by Symbian.
Clearly this doesn’t work well for even this specificdevice let alone for other devices with different start-up sequences.A second possible solution to performing this optimization wouldbe to use the Mediator pattern [Gamma et al., 1994] with the system starter as the Mediator. If this solution were used then the systemstarter would be responsible for starting all the components in the correct order as well as calling the new LoadAllProtocols() functionprovided by the Communications Infrastructure to complete its initialization.
This would move all knowledge of dependencies during start-upto the system starter, solving some of the problems with the first solution.However this still requires each component to expose the details ofits dependencies to the system starter and for the system starter to bewritten with an understanding of these dependencies. While this mightnot initially seem like a big drawback of using the Mediator pattern, itstarts to be more problematic if the start-up sequence changes. Due to thetight coupling between the system starter and the other components in thesystem, when a component changes its dependencies the system starterwould need updating.
A similar thing occurs when a new componentneeds to be added to the start-up sequence.Let’s say, for example, a weather forecast application was added asa priority application which, for example, adds new dependencies tothe protocols that need to be loaded immediately by the Communications Infrastructure. This would require the system starter to makeadditional requests to existing and potentially to new components as partof the device start-up for this new component to be initialized ready forimmediate use by the end user.SolutionThis solution delegates communication between the components thatare interested in a particular state change to a central coordinator.This coordinator has the sole responsibility of informing a group ofcomponents, the responders, in a structured manner that a state changehas occurred and collecting their acknowledgements before moving onthe next group of responders. This allows the understanding of what needsto be done for each change to be distributed to the individual componentswhilst retaining central control over the order in which the notificationsare sent out as well as eliminating the need for each component to beaware of the other components interested in the change.COORDINATOR215StructureThis results in the structure shown in Figure 6.13, where all the objectsinvolved are located in the same thread.
Note the use of Event Mixin (seepage 93) for all the concrete coordinators and concrete responders to bedecoupled.Figure 6.13 Structure of the Coordinator pattern (single-threaded)The controller is responsible for requesting that the coordinator performa state change across all of the responders. It is important to separate theresponsibilities of the controller and coordinator here; the coordinatorshould be entirely agnostic to what the meaning of the state change is,its role is simply to let all the responders know.
All knowledge of whento perform a state change or what to do if one fails should reside in thecontroller. The controller however should never interact directly with theresponders and relies on the coordinator to contact them on its behalf.This separation of responsibilities considerably simplifies the design andallows the system to be loosely coupled.The responders each register with the coordinator for notification ofthe state change but when doing so they specify which group, or layer,that they wish to be notified as part of.
The layers are defined by thecoordinator and are usually ordered such that all the responders at thelowest layer are notified first. It is then each responder’s responsibilityto perform the appropriate actions to ensure that they transition to thisnew state before acknowledging the change. The coordinator waits forall responders at a particular level to acknowledge the change before216PROVIDING SERVICESmoving on to notify the next layer up and so on. Hence the coordinator’sSignalStateChange() method is asynchronous since it potentiallycould take a long time to complete especially if there are a large numberof responders.When using this pattern, it is essential to define two things early on:• the states you wish to send to the responders• the layers into which you want to divide up the responders.These combine to form the state coordination model for how thestate changes will be performed and coordinated. This is an importantdecision to make since it is the one thing that all components involvedin the pattern will depend on, so changing it later will be problematic.
Ifthis model is too abstract then responders will not know how they shouldrespond to each change. On the other hand, if this model is too specific tothe first implementation of the pattern then it’ll result in tighter couplingthan necessary.For most implementations of this pattern within a single thread thereis only one controller and one coordinator. However, this pattern is mostuseful when responders can be found anywhere in the system, even inother processes.
In such cases, an IPC mechanism needs to be introducedto allow this to happen. In order to make it as transparent as possible,Proxy [Gamma et al., 1994] is introduced as shown in Figure 6.14.Figure 6.14Structure of the Coordinator pattern (multiple processes)Responder proxies present the MResponder interface to the coordinator but are implemented to forward these calls over an IPC channel to acoordinator proxy that makes the corresponding call on its local responder.
The calls from the responders follow the reverse path through theseobjects. Note that there is usually one responder proxy and coordinatorproxy per responder since there is normally only one responder per clientCOORDINATOR217process. However, since there are multiple responders in the system, thecoordinator is aware of multiple responder proxies.DynamicsFigure 6.15 illustrates the sequence of messages in a typical interactionbetween a controller, a coordinator and three responders operating attwo different layers. The proxies are not shown, to avoid obscuring thesequence with unnecessary detail as they simply forward messages overIPC and have no other behavior. No process boundaries are shownalthough the three responders are likely to be in three different processes.CControllerCCoordinatorResponderA:CResponderResponderB:CResponderResponderC:CResponderMcRegisterResponderL(ELayer1)McRegisterResponderL(ELayer2)McRegisterResponderL(ELayer1)MscSignalStateChange(ENew)NotifyLayer(ELayer1)MrHandleStateChange(ENew)MrHandleStateChange(ENew)McStateChangeComplete(KErrNone)McStateChangeComplete(KErrNone)NotifyLayer(ELayer2)MrHandleStateChange(ENew)MscStateChangeComplete(aError)McStateChangeComplete(KErrNone)McRegisterResponderL(ELayer1)Responders re-registerfor the next changeMcRegisterResponderL(ELayer2)McRegisterResponderL(ELayer1)Figure 6.15 Dynamics of the Coordinator pattern (successful state change)218PROVIDING SERVICESThe coordinator notifies the registered responders of a change in theorder of their registered layers and not in the order that they register.The coordinator waits for the acknowledgement of the notificationsbefore moving on to send notifications to the next layer.
Only once thecoordinator has received acknowledgements from all the responders doesit tell the controller that the state change has completed. Note that thecoordinator sends all the notifications for a single layer out at the sametime without waiting for each individual responder. This is because weassume that all responders at the same layer are independent of each otherand hence we can optimize the notification procedure by parallelizingbetween the synchronization points between the layers.Figure 6.16 illustrates how a failure should be dealt with during thestate change.CControllerCCoordinatorResponderA:CResponderResponderB:CResponderMcSignalStateChange(ENew)NotifyLayer(ELayer1)MrHandleStateChange(ENew)MrHandleStateChange(ENew)McStateChangeComplete(KErrGeneral)MrCancelStateChange()MscStateChangeComplete(KErrGeneral)Figure 6.16McStateChangeComplete(KErrCancel)Dynamics of the Coordinator pattern (failed state change)When the coordinator receives an error acknowledgement from one ofthe responders it should cancel any outstanding state change notificationsand then call MscStateChangeComplete(error) on the controller.This is an Escalate Errors (see page 32) approach24 to pass responsibility24 Specifically the ‘Escalating Errors Without Leaving’ variant, since we can’t Leave dueto asynchronous calls.COORDINATOR219for resolving the error back to the controller so that the coordinator canbe completely agnostic to the actual meaning of a state change.When the controller receives the error it has a number of choices abouthow to resolve the error.