Symptoms

 < Day Day Up > 



The symptoms of Brittle Bones are much subtler than many of the other illnesses. Often times, they will not reveal themselves until later in development, when a solution is much more difficult to find. Therefore, it is important to look carefully for small signs as they appear. This will allow the problem to be remedied before it becomes a monumental task.

Too Minimal

One sign of a weak foundation is the repeated implementation of similar functionality on top of that foundation. This is similar to erecting a building in a swamp and then piling on extra layers of material to try to fix the sinking. Eventually the house might stabilize and the upper floors will be dry, but the foundation required considerable money and material, which only resulted in a watery foundation. A similar phenomenon occurs with code; as functionality is piled onto the weak foundation, it becomes susceptible to error or complete failure. Even if the resulting application eventually works, programmer time and money were wasted on unnecessary extra work.

Just such a poor foundation class was found in one project that decided against the use of the C++ Standard Template Library. This was already a sign of NIH Syndrome, but the situation was worsened by the implementation of a homegrown list class that was far from complete. The list class that was created implemented only the most basic features such as adding and removing an item from the list. A basic list iterator was also created to go through the list, but offered little functionality that a simple index would not have provided.

 CD-ROM  The list class was similar to the following code, which can also be found on the companion CD-ROM in Source/Examples/Chapter9/minimal.h:

   /**   @warning DO NOT USE THIS CLASS FOR  *   ANYTHING EVER!!!     */    class t_List    {    public:       class t_Iterator       {       public:          t_Iterator(t_List *list)          {             m_list = list;             m_index = 0;          }          void mReset() { m_index = 0; }          void operator++() { m_index = mEnd() ? m_index : ++m_index; }          void operator++(int) { m_index = mEnd() ? m_index : ++m_index; }          void operator--() { m_index = m_index ? --m_index : 0; }          void operator--(int) { m_index = m_index ? --m_index : 0; }          void *mValue()          {             return(mEnd() ? 0 : m_list->mGet(m_index));          }          bool mBegin() { return(m_index == 0); }          bool mEnd() { return(m_index >= m_list->mGetCount()); }          int mGetIndex() { return(m_index); }       private:          t_List *m_list;          int m_index;       };       t_List(int size)       {          m_size = size;          m_count = 0;          m_data = (void**)malloc(m_size * sizeof(void*));       }       void mAdd(int index, void *data)       {          if(m_count == m_size) {             return;          }          if(index < 0 || index > m_count) {             index = m_count;          }          if(index != m_count) {             for(int i = m_count; i != index; i--) {                m_data[i] = m_data[i - 1];             }          }          m_data[index] = data;          m_count++;       }       void mRemove(t_Iterator *iter)       {          for(int i = iter->mGetIndex(); i < m_count - 1; i++) {             m_data[i] = m_data[i + 1];          }          if(m_count) {             m_count--;          }       }       int mGetSize() { return(m_size); }       int mGetCount() { return(m_count); }       void *mGet(int index) { return(m_data[index]); }    private:       int m_size;       int m_count;       void **m_data;    };

The lack of functionality caused the project’s programmers to implement their own search routines in different areas of the code, each with its own unique bugs that had to be fixed individually. If instead the functionality had been contained in the list class, only one place would have to be debugged for the entire application to benefit. This extra debugging time was added on top of the wasted time implementing the same solution multiple times, because it was not obviously available. This again shows a combination of illnesses that began with NIH Syndrome and Brittle Bones and were subsequently complicated by CAP Epidemic and Docuphobia.

Another missing element to the completeness of this list class is the use of templates. Instead, the user of the list is forced to use void*. This leads to a multitude of unsafe type casts and the ensuing errors that these casts always cause. This is particularly problematic with a container class as the insertions can be well separated from the removals that increase the dangers created by the lack of safe typing.

Making this class complete as well as minimal would require substantial additions and changes to the interface. In this case, we are in luck because a minimal and complete interface and the accompanying implementation are available from the C++ Standard Template Library. Although it is not perfect (nothing is) the C++ Standard Template Library provides many examples of well thought out interfaces that fulfill the requirement of being minimal and complete. If you are using C++, this is an excellent set of examples to use in understanding how to make your interfaces minimal and complete. Other languages often have similar libraries that represent good examples of minimal and complete interfaces that can be used for learning to write your own interfaces.

Too Complete

On the opposite side of the coin, it is also possible to provide a foundation that has plenty of functionality stuffed into it, but is still weak and difficult to build upon. One of the biggest problems with too much complicated functionality is the readability of the resulting interfaces and code. As it becomes more difficult to find and understand the functionality available, the programmer must waste time and resources hunting and experimenting to accomplish his goal. Even worse, you might end up with a plethora of methods for accomplishing a task and then find every programmer doing it a different way. This will lead to confusion and more readability issues. Worse still, the programmer might give up altogether and implement the functionality himself. In this scenario, you not only lose the time spent searching and the time spent implementing, you also introduce duplicate functionality that leads to many of the ill effects of the CAP Epidemic.

An example of this type of interface can coincidentally be drawn from a technology that has been redone many times over. This particular complicated interface was part of a user interface library that was being developed to work with DirectX, which did not possess its own user interface functionality. In order to overcome this deficiency, a number of user interface components were created from scratch. One such component was the commonly needed scrollbar. This component is present in almost every windowing user interface and is extremely useful for presenting large quantities of information.

This particular scrollbar was created with, among other things, a horizontal and vertical size. This resulted in a constructor similar to the following:

   Scrollbar(..., int hsize, int vsize, ...);

At first, this might seem like it is necessary to create both a minimal and complete interface, but on closer examination, a problem arises. First, we can create a vertical scrollbar as follows:

   new Scrollbar(..., 0, 10, ...);

In addition, we can create a horizontal scrollbar like this:

   new Scrollbar(..., 10, 0, ...);

However, what happens when we create a scrollbar like this:

   new Scrollbar(..., 10, 10, ...);

This does not make sense using the traditional definition of a scrollbar. Nevertheless, since it was possible to create a scrollbar this way, the library implementer was forced to provide a working control. This control consisted of a large rectangle containing a smaller rectangle that could be moved both horizontally and vertically. While this was an interesting trinket, it was hard to place and of little use in practice. Therefore, although at first glance this interface appeared necessary for a complete interface, the practical uses of scrollbars dictated that this interface violated the principle of a minimal interface and made it too complete.

This interface let to several problems, not the least of which was the loss of time the library implementer incurred by implementing an unused feature. Additionally, the interface was hard to understand and led to several bugs that could have been avoided. The type of scrollbar created was also not immediately evident to other programmers looking at code that created a scrollbar. A better interface would have been:

   enum t_ScrollbarOrientation {       k_IS_HORIZONTAL,       k_IS_VERTICAL,    };    Scrollbar(..., t_ScrollbarOrientation orientation,       int size, ...);

This eliminates the need to implement additional library functionality and as a bonus makes the creation code easier to read:

   new Scrollbar(..., k_IS_HORIZONTAL, 10, ...);

This example also illustrates how the problem domain has a large impact on exactly what fulfills the definition of a minimal and complete implementation. Software development is not an end unto itself, but a method of solving problems most often relating to a specific problem domain.

Lack of Consistency

Another problem that can occur when building a foundation for an application is a lack of consistency in the interfaces built and used for the project. When we refer to consistency, we are talking about consistency within the application and consistency with external interfaces as well. Particularly important is consistency with the language standards and community at large. Violating this consistency can lead to confusion throughout the course of development, often enough to introduce major errors into the code.

For example, C++ allows programmers to overload the meaning of many of the built-in operators in order to allow their use with other object types. When used to create syntactic representations that make sense, such as vector arithmetic, this can make the code much more readable. However, you might see the symptoms of Brittle Bones when this overloading starts taking the form of typing shortcuts. Using addition to indicate the intersection of two sets would be an example of a confusing use of this overloading. The reader would have no intuitive indication of the meaning of addition when applied to the two set objects.

If a programmer saw:

   Set c = a + b;

He might be unsure if this meant to add the members of the two sets, take the union of the two sets, take the intersection of the two sets, or perhaps something else entirely. While there might be a reasonable chance that the programmer would pick the correct meaning, the ambiguity could be eliminated with only a few more characters:

   Set c = a.union(b);

The meaning of vector addition, on the other hand, is well accepted; therefore, overriding the addition operator for vectors is reasonable:

   Vector c = a + b;

However, overriding the multiplication operator can be problematic, as some programmers might mistake it for a dot product operator. Thus, even in the simple case of vector multiplication, it is better to spell out the operation than overload an ambiguous operator.

Even consistency in naming is important for a solid foundation. In Java, querying the boolean state of a particular aspect of an object is usually done with a function that is prefixed with is. Violating this convention is another inconsistency that will lead to problems throughout development. Developers will have to search harder to find the function in an unexpected location, or worse, they might go ahead and write their own version of the function.

Returning to the example given earlier when we talked about overly minimal interfaces, there is also a consistency problem with the operation of the list class. Once again, this relates to the problem of attempting to reinvent the technology already existing in the C++ standard template library. In this case, the iterator class violates the convention used for list iterators in the standard template library that says they are invalidated when the element they refer to is removed from the list. This will cause confusion among the other programmers on the project, as the iterator will not behave as they expect if they have used the standard template library. Because it resembles the iterator from the standard template library, this becomes a major problem. Further, once the developers have adapted to using this new style iterator, they will litter the code with uses that will not allow them to change over to the standard template library despite the fact that it would appear that such a change would be simple. This is made more detrimental because the list class is lacking functionality that could have easily been added by performing such a switch.

Spiraling

Whatever has caused the foundation to become brittle, one symptom that will always manifest is a cascade of problems that build upon each other. This spiraling loss of control starts with the foundation and becomes worse as each layer is built on top of the last (Figure 9.1). Unfortunately, this symptom is not obvious until much later in development and is therefore akin to the final stages of a disease. Just as with a disease, this can easily be a harbinger of the project’s death unless immediate action is taken. Even with immediate action, an irrecoverable cost has already been paid at this point. Therefore, be diligent and work to avoid ever seeing this symptom except in its first subtle stages.

click to expand
Figure 9.1: Illustration of how a single weakness in the foundation can be built upon to create further weaknesses throughout development. This often obfuscates the fact that the weakness originates from the foundation, making this illness even more difficult to diagnose.



 < Day Day Up > 



Preventative Programming Techniques. Avoid and Correct Common Mistakes
Preventative Programming Techniques: Avoid and Correct Common Mistakes (Charles River Media Programming)
ISBN: 1584502576
EAN: 2147483647
Year: 2002
Pages: 121
Authors: Brian Hawkins

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net