Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 32
Текст из файла (страница 32)
An example of a stateful softwareservice is a component that provides read and write access to a databasesince the service may have to track any changes to the database up untilthe client calls commit or rollback. An example of a stateless service is amath library which provides actions such as returning a random numberor the square root of a number.A key reason for having services is that they offer software reuse whichcan be used to build software more quickly, more efficiently and morereliably. The alternative to using a service is to create your own standalone copy for each client which quickly becomes costly to develop. Thelong term costs of duplicated code can also be significant as not onlyis there more code to support but any additional functionality must alsobe replicated.
As if that wasn’t enough, the replication increases codesize and the associated RAM impacts could easily be significant on aresource-constrained device such as a smartphone.166PROVIDING SERVICESA well-designed service has the following properties:• Cohesion – a measure of how strongly-related and focused the variousactions offered by the service are.
A highly cohesive service enforcesits logical boundaries.• Separation of concerns – the service overlaps in functionality as littleas possible with other services available to clients.• Encapsulation – the design decisions on which the service is basedare the ones that are most likely to change are hidden from clients.Well-encapsulated services provide a stable interface which shieldsclients from changes in how the service is implemented.• Extensibility – how well the service supports future growth whilstremaining backward compatible.
Backwards compatibility is wherethe clients of an old version of the service can run on later versions ofthe service without being changed.• Usability – an indication of how easy the service is to use whichgenerally reflects the elegance and clarity with which the interface tothe service has been designed.These properties focus on making life easier for the client developers;the more clients a service has, the more important these propertiesbecome.There are two main ways in which the actions of a service canbe provided to clients: synchronously and asynchronously. An actionoffered by a service may take any amount of time from nanoseconds fora couple of instructions to many seconds or more. If the time taken is notsignificant, a client may wish to choose to make a synchronous requestto the service that blocks the client’s execution until the action has beencompleted.
A function which simply calculates and then returns a valueis an example of this. Using a synchronous request allows a client tosimplify its design. However where the action may take a considerableamount of time an asynchronous request may be needed to guaranteesome quality of service, such as responding to events generated by theend user. Figure 6.1 illustrates the difference between the two types ofactions.The main difficulty with asynchronous requests is that they introduceadditional execution paths for clients to deal with.
This is particularly difficult for them as they are not able to predict in advance which executionpath will be taken at run time. To help reduce complexity for clients, werecommend you follow a consistent approach for asynchronous requestsas follows.The asynchronous request always has two responses: the immediatesynchronous ‘request made’ response that simply indicates a request hasbeen received by the service; and the asynchronous ‘request complete’PROVIDING SERVICES167Figure 6.1 Synchronous and asynchronous service requestsresponse at some point later that indicates that the request has actuallyfinished.
All errors in carrying out the service request should be returnedthrough the ‘request complete’ response. This helps prevent the duplication of error-handling code for clients. This may mean the request is infact completed immediately if an error occurs whilst making the initialservice request.We also need to consider how to allow clients to cancel an asynchronous request. This is where a client makes a synchronous requestto ask that a previous asynchronous request should be completed assoon as possible. Figure 6.2 shows the most obvious sequence diagramfor a cancellation in which the original asynchronous service request isprevented from succeeding and completes with a KErrCancel to showthat it was stopped prematurely.An alternative sequence (see Figure 6.3) is that the client sent the cancelservice request too late and the service has finished performing the actionoriginally requested.
The result is that the asynchronous service requestcompletes with KErrNone to indicate the action originally requestedwas performed.There are even more alternative execution sequences, such as theasynchronous service request completing with an error that isn’tKErrCancel or KErrNone since the action had time to complete168PROVIDING SERVICESFigure 6.2 Asynchronous service request stopped early enoughFigure 6.3 Asynchronous service request couldn’t be stoppedbut some other error intervened, or the client has sent a cancel whenthere isn’t a service request outstanding.We recommend service providers follow a consistent approach forcanceling asynchronous requests as follows:• Cancellation requests are full requests in their own right separatefrom the asynchronous request they are canceling.
Hence, there is aPROVIDING SERVICES169‘request complete’ response for the cancellation separate from one forthe original asynchronous request.• Cancellation requests are always synchronous. This is to ensure thata cancellation can be used in the destructor of an object to cleanup the resources it is using. It doesn’t make sense to try to stop anasynchronous request with another asynchronous request.• The asynchronous request being canceled must be completed beforethe cancel request itself is completed otherwise the client may havebeen deleted prior to the completion of the second request completion.• The error message returned with the completion of the asynchronousrequest should reflect whether or not the original request was successful.
That is, if the request went ahead and the action was performedsuccessfully then KErrNone should be used; if the action was stoppedprematurely, KErrCancel should be used.It is important to remember that cancel does not mean that any actionthat the asynchronous request has already performed should be undone.If you need such rollback semantics then canceling is only the first stepyou need to take.Note that as a client of a service you may not need to deal with eachand every message from the service yourself and hence it might appearas if this approach is not being used. This often results from servicesthat provide helper objects, based on the Proxy pattern [Gamma et al.,1994], that clients use to access the service indirectly. One example ofthis is CActive::Cancel(), described in Active Objects (see page133) where calling this function does not result in the object’s RunL()being called to handle the completion of the original request.
Thismakes it appear as if the original request wasn’t completed. The reason for this is that the Symbian OS active object framework choosesto simplify the majority of active objects by assuming they wish toignore the completion of an asynchronous request after it has beencanceled.The patterns in this chapter describe some techniques for providingservices following these principles. The simplest is Client-Thread Service(see page 171) which describes how to provide a service that executeswithin the context of the client’s thread. This allows for a simple designbut is not suitable for more complex services which need to provideaccess to a shared resource or must check the security credentials ofclients before accepting requests from them.Services which require such functionality should consider usingClient–Server (see page 182) to solve their problem.
This pattern providessupport for clients in separate threads by allowing them simple and synchronized access to the resources and actions they require. This pattern170PROVIDING SERVICESis also well suited to enforce any security policies required by a serviceprovider.Last but not least, Coordinator (see page 211) describes a techniqueused by services to allow multiple interdependent clients to receivenotification of state changes that they need to react to in a structuredmanner.CLIENT-THREAD SERVICE171Client-Thread ServiceIntentEnable the re-use of code by rapidly developing a service that executesindependently within the thread of each client.AKAShared Library1ProblemContextYou wish to develop a service that can be re-used by a number of components, with clients of the service able to operate independently of eachother.Summary• It is expensive to create stand-alone code for each application thatneeds it.• Maintenance of multiple copies of such code usually doesn’t scalewell and is defect-prone.• Duplication of common code is memory inefficient in terms of bothcode size and RAM usage.DescriptionNovice software developers often re-write similar code for each application – code that essentially performs the same actions and which mayeven be copied and pasted between projects.This is a problem, and not just because re-writing code is expensive(and boring).