August 21, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Simplifying Access to BREW Databases

  • May 18, 2005
  • By Ray Rischpater
  • Send Email »
  • More Articles »

Sharing Database Fields with Object Attributes

The obvious thing to do is to wrap the code that glues the application data model to the underlying BREW database, if only so that it's easy to find. Most BREW application developers, working in C, would be tempted to simply create a module to convert from one representation to another. There is a better way, however, that closely resembles how most C++ developers would tackle the problem, and it's easily implemented in C.

The approach builds on the BREW component model, making the application data model—in my example, the ITaskRecord—act like a BREW interface with methods, but share its data members with the underlying AEEDBField array. Doing this has three key advantages over a more traditional C module, or even a typical C++ class:

  • For users of the record component as a model, the component can retain its appearance as a conventional C structure, completely hiding its nature as a client of the uglier aspects of the IDBRecord interface.
  • The object can implement the remainder of the IDBRecord interface, and a second component can extend the IDatabase interface, providing a well-defined interface to the data store that's understood by most BREW developers with little implementation difficulty.
  • The overhead of a virtual table for the record component is typically much smaller than the code and memory overhead associated with the skullduggery typically performed when converting a C structure to an IDBRecord and back.
  • Because the model and its hooks to the database are well-isolated, it's easy to add new fields to the database record implementation as you flesh out the requirements for your application.

To users of the object, the interface becomes one that looks like a BREW extension (see my previous article), as you see in Listing 3. The only significant addition to the ITaskRecord you saw previously is a pointer to the object's virtual table (also known as a vtable) that implements the appropriate subset of IDBRecord methods, including AddRef, Release, UpdateRecord, RemoveRecord, and an additional method, SetContentsOfRecord, that several of my developers found helpful when using the object. Of course, I accompany the vtable with the usual helper macros, making it easy to access each of the methods directly. In essence, by sharing the data field with the underlying IDBRecord implementation the C structure provides a proxy to the underlying database representation.

In designing the vtable, you might wonder why I didn't provide accessors and mutators for each of the fields in the class. To be sure, the idea crossed my mind, and in many settings it's probably a very good one. However, in designing this interface I wanted an object with as close an appearance as possible to a C structure. Furthermore, I didn't want the overhead—on the part of either the programmer or the source code base—of getters and setters for each field. Although not onerous for a three-tuple, I've applied this trick with thrice as many fields, and hand-coding and unit testing the accessors and mutators in C for each field is almost as error-prone as letting other developers have direct access to the data fields. It's admittedly a compromise, and if I had to do it again, I might choose to do it differently; similarly, you should consider whether to provide raw access to fields or protect access through methods when designing your record object as well.

Under the hood, the data representation of the ITaskRecord is a trifle more complex because in addition to carrying around the fields of a task record, it also carries around the necessary data to perform as a BREW database record, including the AEEDBField array used when creating or updating the record.

Listing 3

typedef struct _STaskRecord_private
{
   IDBRecord            *pIDBRecord;
   AEEDBField           aFields[TASKRECORD_MAXFIELDCOUNT];
   uint32               nRefs;
} STaskRecord_private;

typedef struct _STaskRecordData
{
   ITaskRecord          public;
   STaskRecord_private  private;
   ITaskRecordVtbl      vt;
} STaskRecordData;

The private implementation of an ITaskRecord must be cast-compatible with the public interface, so the private representation STaskRecordData ensures this by placing the public ITaskRecord first, followed by a second structure for its private data and finally its vtable. (I could have chosen to simply lump the private fields after the public member, but encapsulating the private data in its own structure adds clarity at the expense of a little typing.) The private data must carry the record's BREWuff of database fields (an AEEDBField array) along with the IDBRecord associated with the record; in addition, the ITaskRecord implements the full BREW IBase reference counting schema, so it must carry a reference count as well.

The implementation of the actual ITaskRecord class is straightforward, as you can see from Listing 4. Rather than walk through each method, I invite you to examine the listing and see how it works for yourself; only a few additional explanations are necessary.

First, because each ITaskRecord carries its AEEDBField array along with it, the ITaskRecord_Update method is simply a wrapper around the IDBRecord_Update method, which commits each field (pointing back to the public members of the ITaskRecord structure) to the data store.

Second, ITaskRecord_Remove asserts that the reference count of a record to be deleted is unity, so that a programmer doesn't accidentally pull the rug out from the application when more than one part of the application is relying on the record. Using ASSERT is a bit of a cop-out, however, because it only points out programming errors in debug builds; it's still a potential issue in production releases. Frankly, I never did decide whether I wanted to let other developers hang themselves by removing a record in use in multiple places in the code; the design of the application is such that it would be nigh impossible. Bear in mind when crafting your own solution, though, that it's an area that deserves some additional analysis.

Finally, ITaskRecord_Release is a little tricky because it must decrement both the ITaskRecord's reference count and the underlying IDBRecord, and free the ITaskRecord only if its reference count is zero. Note that ITaskRecord_Release doesn't update changes to the data store—doing so would violate the contract specified by the IDBRecord interface, which only changes the contents of its records when you invoke IDBRecord_Update.





Page 2 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel