IT tutorials
 
Mobile
 

Mapping Well-Known Patterns onto Symbian OS : Handle–Body

2/4/2012 4:15:34 PM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019

1. Problem

1.1. Context

One of your components needs to expose an interface for use by external clients.

1.2. Summary
  • You wish your component's interface to be as encapsulated as possible.

  • You wish to maintain the compatibility of the exposed interface across multiple versions of your component whilst retaining the freedom to change how it is implemented.

  • You would like to rapidly develop a component but also allow for optimization or the addition of new features later.

1.3. Description

As the software in mobile devices becomes ever more complicated, it also becomes more difficult to both maintain and extend it.

You will often find yourself asking questions such as:

  • Does my software implement a specification that is subject to changes and updates?

  • Will I need to allow a different implementation to be provided?

  • Do I need to cope with different versions of software?

  • Do I need to develop an early version of a component as soon as possible which is'good enough' now but will need to be re-factored later on?

The problems of developing software that is easy to expand, maintain and re-factor has and always will be a key issue that you will face. A standard way to achieve this is to ensure your interfaces are well encapsulated and don't 'leak' any unnecessary details or dependencies especially when external clients are relying on the interface to remain stable and not change in any way that will stop them from working correctly.

But how can we do this in C++? There are a number of ways to accidentally break clients. For instance, you'd think that adding a private data member to a class would be safe since it's private after all. However, doing this increases the size of your class and any client code that calls a class constructor relies on that being fixed. Indeed one of the benefits of the second-phase construction common in Symbian OS is that clients don't get to call the constructor directly. Instead they get passed a pointer to the class and hence don't depend on the size of the class which allows it to be changed. Unless it's an abstract class and clients are expected to derive from it – then they have to call your class's constructor which makes them sensitive to its size again.


Whether you are developing low-level kernel modifications, middle level services or end-user applications, the ability to be able to change software quickly and easily will make your life as a developer much less stressful!

1.4. Example

The HTTP framework provided by Symbian OS supports HTTP requests and HTTP responses. A simple design would implement these as two distinct classes as conceptually they are used for different purposes. One factor influencing the design would be that the requests and responses form part of a well-known protocol that is unlikely to undergo any major changes.

The framework needed to be developed in an iterative fashion so that clients had at least some HTTP functionality as soon as possible. Additional features were then added later. For example, the very first version of the HTTP framework only supported HTTP GET. It would take a further nine months and five iterations to fully develop a framework which had all the required functionality that a web browser requires.

Although the two-phase construction idiom used in Symbian OS means that the size of the classes could be increased without breaking compatibility, it was felt that this still exposed too much of the underlying implementation details to the clients. If one mistake was made that meant clients accidentally relied on the details of the implementation rather than the interface this would've caused serious problems down the line. A way of further separating the implementation of the classes from its interface was needed.

It was the uncertainty of the future design and the realization that it would almost certainly need to change that lead to the use of this pattern not just for the request and response classes but across the entire HTTP framework.

2. Solution

This pattern supports and encourages encapsulation and loose coupling by hiding the implementation details from your clients by dividing one class into two. The first of these, the handle, simply defines the interface and then defers all implementation of that interface to the body.

A point to remember is that the word 'handle' being referred to here does not necessarily imply the use of a Symbian OS handle derived from RHandleBase such as RThread or RProcess.

2.1. Structure

A client is only ever able to see the interface class and just uses that directly. However, the interface class delegates all calls through a pointer to the underlying handle which provides the implementation as shown in Figure 1.

Figure 1. Structure of the Handle–Body pattern

This particular technique is often referred to in C++ as the 'Cheshire Cat Idiom', named after the character from Lewis Carroll's Alice in Wonderland, and refers to the point where the cat disappears gradually until nothing is left but its smile. Here the smile is the implementation pointer.

If a data member is added to the body class then the size of the handle class remains the same since its size is just that of a pointer to the body. Hence the client does not need to be recompiled as it is isolated from this change.

The handle and the body classes are normally defined in the same DLL but only the handle is exported. This binary firewall allows the client to be shielded from any changes in the body. This is particularly advantageous if you would like to vary the details of the implementation depending on the situation. For instance, if the interface is used to store and retrieve objects, the implementation might need to change depending on exactly how many objects you expect clients to store. This allows your interface to provide the best performance for each situation.

2.2. Dynamics

The dynamics of this pattern aren't very complicated as the handle simply forwards all calls to its body as shown in Figure 2.

Figure 2. Dynamics of the Handle–Body pattern

There is one additional subtlety that should be mentioned though and that's the fact that by forcing the body to be allocated on the heap you may have introduced a new error path for the client to deal with. Consider for a moment that you weren't using this pattern and instead provided an exposed interface as a T class used solely on the client's stack. Changing the design to use a pointer to a body class instead would mean that clients now also have to deal with the handle possibly failing to be created which wasn't a problem previously.

2.3. Implementation

To use this pattern you need to separate your interface from your implementation. If you have an existing class that you wish to convert to using this pattern then you need to scoop out all the private data members, private functions and implementation of the original class and put them to one side for now. If you add a pointer to a CBody class, which we'll define later, then you have the beginnings of your handle class.

You then have the choice of implementing your handle as either an R or a C class.[] For this illustration, we've chosen to use a C class to avoid obscuring the main thrust of the pattern with the additional considerations that you need to take into account when using an R class.

[] It can't be an M class as it has a data member, nor can it be a T class as the data is on the heap.

Next you should forward declare your body class in the header file used to define your handle class. This avoids you having to #include any header files for the body and is another means of ensuring the interface is independent of its implementation. It also incidentally reduces the amount of code a client needs to compile when using the interface.

This gives the following header file for the handle class:

class CBody;

class CHandle : public CBase
{
public:
IMPORT_C static CHandle* NewL();
IMPORT_C void DoSomethingL();
private:
CHandle();
private:
CBody* iBody
};

The source file for the interface simply has to forward any calls on to the body.

EXPORT_C void CHandle::DoSomethingL()
{
iBody->DoSomethingL();
}

Some additional points to note include:

  • No handle functions should be declared as inline. Whilst it is tempting to do this as their implementation is so simple, it means you will not be able to change the function ever again without breaking binary compatibility. This is because an inline function puts the code for the function in the client's binary[] and not yours, which breaches the binary firewall that we are trying to erect.

    [] Whilst it's true that sometimes functions marked as inline aren't actually inlined by the compiler, it can happen and so we have to treat the functions as if they will actually be inlined to avoid any risk of breaking compatibility.

  • The handle class normally does not contain any private functions except for the constructor functions. Instead all such functions should be implemented by the body class.

  • Do not implement the body by providing virtual functions in the handle class and then deriving the body from the handle. Whilst this makes the implementation of the handle slightly simpler, it effectively destroys the benefits of this pattern by exposing the size of the class to further derived classes; introducing vtables which are tricky to maintain binary compatibility for, and preventing alternative bodies to be used at run time.

  • No ConstructL() function is needed as normally you would just call a function on the body that returns a fully constructed object.

2.4. Consequences

Positives

  • This pattern allows an interface to be extended without breaking pre-existing clients.

    Over time, you will need to extend the interface to add new functionality. This pattern gives you more flexibility when you need to extend your component because it removes restrictions that you would otherwise have felt. For instance, you are completely free to change the size of the implementation class even if the interface has been derived from. Most changes to the implementation are hidden from clients.

  • This pattern reduces the maintenance costs for your component by allowing an implementation to be replaced or re-factored without impacting clients of your interface.

    Over time, code becomes more difficult to maintain as new features are added. At some point it becomes more beneficial to completely replace, re-factor or redesign the existing code to allow it to perform better or to allow for new functionality to be added. Where this pattern has been used, this is significantly easier to do because the interface is well encapsulated so that as long as the interface remains compatible, the underlying implementation can be completely re-factored or replaced.

  • You have the freedom to choose a different implementation.

    Since this pattern hides so much of the implementation from clients of your interface you have more freedom to select the most appropriate body for the circumstances. This can be done at compile time but also at run time for even more flexibility. If you do need to dynamically load a body then you should use the Symbian OS ECom plug-in service.[] Remember that, in either case, each of the different bodies needs to provide the same overall behavior as it's no good if clients still compile but they then break at run time because your interface performs in ways the client didn't expect.

    [] See [Harrison and Shackman, 2007, Section 19.3] for details about ECom.

  • Your testing costs are reduced.

    Since the interface to your component is well encapsulated you will have fewer side-effects to deal with during testing of the component using this pattern. However, this pattern also makes it easier to create mock objects[] when testing other components by making it simple to replace the real implementation with a test implementation.

    [] en.wikipedia.org/wiki/Mock_object.

Negatives

  • Additional complexity can increase maintenance costs.

    If you hadn't applied this pattern then you'd have one fewer class in your design. Whilst this doesn't sound like much, if you apply this pattern to a lot of interfaces then you can get into a situation where class hierarchies can be quite complicated. This can make it harder to understand and debug your component as a result.

  • Additional overheads are caused by the extra indirection.

    By introducing an additional level of indirection this incurs small execution-time and code-size penalties for your component. The extra function call used when calling the body class, and the extra object allocation, leads to the loss of some CPU cycles whilst the extra class requires more code.

2.5. Example Resolved

As seen in the solution above this is a very simple pattern, but it is always worth looking at how it has been used in a real example.

As described above, Symbian OS provides an interface for clients to use HTTP functionality. One of the interfaces exposed by this component is the RHTTPSession class which represents a client's connection with a remote entity and allows multiple transactions to take place using this connection. This class is the starting point for developers using the Symbian OS HTTP service.

Since it was important to provide a stable interface to clients whilst not restricting future growth, this pattern was used.

Handle

RHTTPSession is the handle as it provides the interface used by clients. In this case it is an R class to indicate that it provides access to a resource owned elsewhere, which in this case is in the HTTP protocol itself.

The following code shows the most important points of this class from the point of view of this pattern. The full details can be found in epoc32\include\http\rhttpsession.h.

class CHTTPSession; // Note the forward declare of the body

class RHTTPSession
{
public:
IMPORT_C void OpenL();
IMPORT_C RHTTPHeaders RequestSessionHeadersL();
...
private:
friend class CHTTPSession;

CHTTPSession* iImplementation; // Note the pointer to the body
};

The following is the simplest of several different options for beginning an HTTP session. This method, OpenL(), acts as the factory construction function for RHTTPSession and allows clients to create and initialize a session with the HTTP service.

This function call constructs the body by simply calling its NewL(). Slightly unusually, the handle function, OpenL(), has a different name to this body function. This simply reflects the fact that the handle is an R class whilst the body is a C class which means developers wouldn't expect to see a NewL() on RHTTPSession whilst they would on CHTTPSession. The functions also act very slightly differently in that OpenL() is called on an existing object whilst NewL() is a static function and doesn't need to be called on an object.

Finally, note the use of Fail Fast (see page 17) to catch clients who call this function twice which would otherwise cause a memory leak.

EXPORT_C void RHTTPSession::OpenL()
{
__ASSERT_DEBUG(iImplementation == 0,
HTTPPanic::Panic(HTTPPanic::ESessionAlreadyOpen));
iImplementation = CHTTPSession::NewL();
}

The following shows an example of one of the handle's functions that simply forward the call on to the body to deal with.

EXPORT_C RHTTPHeaders RHTTPSession::RequestSessionHeadersL()
{
return iImplementation->RequestSessionHeadersL();
}

Body

The CHTTPSession class forms the body in this use of the pattern as follows:

class CHTTPSession : public CBase
{
public:
static CHTTPSession* NewL();
RHTTPHeaders RequestSessionHeadersL();
inline RHTTPSession Handle();
...
private:
CHeaderFieldPart* iConnectionInfo; // The proxy or connection details
RArray<CTransaction*> iTransactions; // All the open transactions
RArray<THTTPFilterRegistration> iFilterQueue;
...
};

The NewL() isn't shown because it is implemented in the standard Symbian way as a two-phase constructor. However, here is the implementation of CHTTPSession::RequestSessionHeadersL() which provides the functionality for RHTTPSession::RequestSessionHeadersL().

Note the use of Lazy Allocation (see page 63) to create the request header collection class on demand. Apart from that and returning an object on the stack from the function, there is nothing unusual here.

RHTTPHeaders CHTTPSession::RequestSessionHeadersL()
{
// Create the session headers if necessary
if (iRequestSessionHeaders == NULL)
{
iRequestSessionHeaders = CHeaders::NewL(*iProtocolHandler->Codec());
}
return iRequestSessionHeaders->Handle();
}

The one additional function we should discuss is the Handle() function provided by the body which returns an RHTTPSession instance which uses the current object as its body. Providing a Handle() function on a body class may look slightly odd at first but points to an interesting side-effect which the HTTP framework takes full advantage of.

If you take a closer look at RequestSessionHeadersL(), you will see that it returns an object by value rather than a reference or pointer to one. This is because RHTTPHeaders is also implemented using this pattern and is a handle to a CHeaders object. This means that RHTTPHeaders is an object of size 4 bytes, because of its iBody pointer, which makes it efficient to pass by value on the stack.

The CHTTPSession::Handle() method works in the same way and creates a new handle to itself. The function can be inlined because it's only ever used within your DLL and so there's no danger of it leaking out into client DLLs and causing compatibility problems in the future. If you were wondering why CHTTPSession was declared as a friend by RHTTPSession it's so the following would be possible:

inline RHTTPSession CHTTPSession::Handle()
{
RHTTPSession r;
r.iImplementation = this;
return r;
}

This isn't an essential part of implementing this pattern but can be useful in other implementations as you can use the handle class in a similar way to the way you would treat a pointer to the body class but with more protection. Note that this all works because only shallow copies of the handle class are made. Hence copying a handle could mean that there are two handles pointing to the same body just as if you'd copied a pointer. This means the ownership of the handle classes should be treated carefully to avoid double deleting the body.

3. Other Known Uses

  • Image Conversion

    This component makes use of this pattern in both CImageDisplay and CImageTransform to provide these widely used classes with the flexibility to be extended in future to allow different image formats to be displayed and transformed through the same interface by simply changing the underlying body providing the implementation of the interface.

  • ECom

    The Symbian OS ECom plug-in service is based on this pattern as frameworks interact with plug-ins through a handle class. A plug-in provider then provides a body class to implement that handle.

 
Others
 
- Mapping Well-Known Patterns onto Symbian OS : Adapter
- Mobile Web Apps : Loading Pages (part 3) - Going Backwards
- Mobile Web Apps : Loading Pages (part 2) - Sliding
- Mobile Web Apps : Loading Pages (part 1) - Swapping Pages & Fading with WebKit Animations
- Personalize & Secure Your iPad : Adjusting Sounds on your iPad & Personalize Your Picture Frame
- Personalize & Secure Your iPad : Changing your Lock Screen and Home Screen Wallpapers
- Using Media in XNA Game Studio : Media Enumeration
- Using Media in XNA Game Studio : What Is Media?
- Android Application Development : Layouts (part 2) - AbsoluteLayout & RelativeLayout
- Android Application Development : Layouts (part 1) - inearLayout
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us