| Intro | Part I | Part II | Part III | Part IV | Part V (4.1) | Part VI (6.0) | Summary |
As we all should know by now, there are three types of maintenance: corrective, adaptive, perfective. Roughly translated, that's bug fixing, extension to support external requirements and extension to support new functionality. Funnily enough, customising a third-party OO library usually looks like this too - we are simply maintaining the vendor's library! We shouldn't be fixing their bugs, we shouldn't even need to do any adaptive work as the library should be flexible enough to accommodate all sorts of changes in the external environment. Since we are building on the library, we should expect to undertake perfective maintenance only. Unfortunately, if a library isn't designed to be extensible then we end up doing adaptive maintenance just to support our own perfective maintenance.
BroadVision supply the source to their integrators because even they themselves admit that you can't really build on the framework without access to the internals - to see how their existing objects work. Our approach to maintenance was to put the whole source code under version control and use SNiFF+ as a source browser and editor since it integrates well with almost any version control system (we use RCS) and make system (we use UNIX makefiles and Sun's C++ SPARCcompiler). Then, whenever we needed to modify a BroadVision object, we copied the source, renamed the class from Dyn_Xxx to ISS_Xxx and fixed the code in that version instead. This allowed us to integrate changes made by BroadVision in subsequent versions of their framework into our own code as and when we needed - some bugs got fixed by BroadVision and some didn't as they went from version 2.5 to version 2.6 to version 3.0.
int equal_string(const char* s1, const char* s2, int ignoreCase = 1 );When an object processes the attributes in an HTML tag, it has code like this:
if ( equal_string( attr.name, "HEIGHT", 1 ) ) // store attr.value to height member variableUnfortunately, in version 2.5 the code wasn't very consistent, sometimes comparing lowercase attribute names (without the ignoreCase parameter), sometimes comparing uppercase attribute names (with the third parameter supplied as 1) and in one unfortunate case the code had:
if ( equal_string( attr.name, "WIDTH, 1" ) ) // store attr.value to width member variableThis meant that the object in question didn't recognise the WIDTH=nn attribute on images. By version 3.0, BroadVision had updated all their code to use the three argument version of equal_string. I suspect they had started in an early version of the framework with a two argument version of equal_string and added the ignoreCase argument in a later version as a default to provide backward compatibility. Moral: default arguments can be a Bad Thing(tm)!
It should be obvious why I consider this corrective maintenance: the code was simply incorrect.
Why do I consider this adaptive maintenance? BroadVision didn't anticipate that users would need very fine control over the formatting of output. In particular, for the travel site, there was a legal requirement covering the display format of currency rates - an external requirement not anticipated by the designers.
However, there was another issue at play here: database field display objects had to convert their data from a different internal format, a "run-time value" format, before converting the actual value to the required display format. BroadVision's approach was to have two sub-hierarchies of classes, one with its root at Dyn_IOField for formatting 'normal' values and the other with its root at Dyn_ContentField (itself inherited from Dyn_IOField) for formatting database values. No wonder their code contained so much duplication and the formatting was rather haphazard!
Our approach was, as usual, to parameterise the display objects so that they knew whether to convert the internal void* value as a "run-time value" first, before applying the formatting. We simply added a new attribute CONTENT=YES to indicate the conversion should be applied first.
A better approach for BroadVision might well have been to separate the value-fetch functionality from the formatting functionality and allow the value-fetch objects to invoke another named object to perform the formatting after any internal conversion. C++ makes this sort of run-time dynamic behaviour somewhat difficult however!
This is perfective maintenance because we could have compromised the user interface design in order to fit in with the supplied functionality. Multiple select drop-downs were a "nice to have" that could have been implemented as a series of checkboxes. The text area field could have been implemented as a long, scrolling text field. Checkboxes and simple text fields are part of BroadVision's basic functionality. We wanted to extend the core functionality to support "prettier" web pages.
Here's where the two sites differed greatly. For the travel site, we retained the logical data model within the database, using a large number of tables outside BroadVision's product table. The context engine for that site incorporated database accessors that searched for relevant content within specific tables, determined by the current context. In fighting against the framework, we ended up writing a lot more code. For the car manufacturer's site, we mapped the logical data model down to BroadVision's flat product table model - working within the framework - and then our context engine needed only to generate the SQL query which searched that table. We used BroadVision's standard database accessor to perform the search using the generated SQL. This allowed us to take advantage of BroadVision sophisticated database caching techniques as well as the observation machinery it provides to track which products a visitor is shown and which they select for more information.
As a side-effect, this allowed our context engine to be more 'generic' - it depended much less on BroadVision architecture of accessors, loops, field display objects and so on. As a consequence of this, the context engine was more Bean-like, to use a term borrowed from Java: the external interface - to BroadVision - was smaller and better-structured. In fact, we used a Java-based CASE tool, Together/J from Object International, to design and build a prototype context engine in Java. Before we even wrote a single line of code, we had a fully-featured working Java model of how the context engine would behave. Recoding this into C++ was a straightforward, if somewhat tedious, process. The result was a set of classes that worked first time. The design even proved to be very resilient to some fairly dramatic changes we made to the generated SQL queries in order to tune the database access.
The engine underlying the car manufacturer's site is a better design and takes better advantage of the base functionality provided by BroadVision. We were able to write fewer objects that extended the framework and concentrate on solving the real problem at hand: maintaining a visitor context. I'll probably write a future article about my design principles to show why I think one design was better than another.
BroadVision have recently released version 4.0 of their framework which, whilst
it supports version 3.0 objects, takes a radically different approach to customisation.
In version 4.0 they have opened up the object model in such a way that it can
be manipulated by server-side JavaScript. Instead of writing custom objects
to perform conditional generation of HTML it should be possible in version 4.0
to achieve this by using JavaScript on the pages. This is a much more cost effective
approach: C++ skills are much more expensive than JavaScript skills in the web
programming world. It will make BroadVision an easier product for web developers
to use. We're fairly confident that the approach we've taken with the car manufacturer's
site will also be a better match to this new direction since it relies less
heavily on customising version 3.0 objects.
Update: see my comments
on version 4.1.
On the other hand, selecting an application framework will shape the way you work. Most frameworks have a steep learning curve and are best used utilised when you go with the grain rather than trying to force it to your will. This means that you need to ensure the framework fits your application area very well or that you know how to shape your application to your framework of choice. Evaluating an application framework is often very difficult because you can't tell how much it will help you until you've actually built something with it. Conversely, you can't tell how to build something with it until you've learnt a lot about how it works itself.
I still think application frameworks are a "necessary evil" but they are getting better. We couldn't have built either the travel site or the car site as quickly without BroadVision. Or we could have chosen to reduce the functionality but that makes us less competitive as companies expect more and more from a web site.
Some of you may well be wondering why this final installment is so late. After completing development of the car manufacturer's site at the end of last year, I immediately became involved in another BroadVision project that had very tight deadlines and I simply didn't have time to work on this article. The new project involves no C++ at all, just look'n'feel customisation of a product BroadVision calls "Knowledge Web Application". It's ideal for Intranet and Extranet projects which deliver masses of content - documents - within a standard navigation framework. KWA provides the navigation and the content management system. You just set up the database structure to reflect how the content is classified and how it should be delivered in "channels" and "programmes" within those channels, then you can start publishing content. That site is now installed at the client offices and only client training and "go-live" support remains.
But don't think I have time on my hands! I've just started yet another BroadVision project which will start by auditing an existing BroadVision-powered site which takes little advantage of the framework's power. My job is to determine a roadmap towards a fully dynamic, personalised version of the site.
However, I do intend to write another article that follows on from this series if the editors are interested. It will look at BroadVision's main competitor, ATG's Dynamo, which is written entirely in Java and is used to power sites such as Sony Entertainment (the PlayStation site) and My Sun (a personalised portal site for Sun's other web sites). It takes a radically different approach in terms of design and architecture - an architecture made possible by Java. Essentially I want to highlight how Java and C++ lend themselves to very different design strategies - something that I've been dealing with quite a lot since I started using Java as a design language even for systems that would be built in C++.