Solving with Special Cases

Given the two different CAD/CAM systems described in Chapter 3, “A Problem That Cries Out for Flexible Code,” how do I build an information-extraction system that will look the same to a client object regardless of which CAD/CAM system that I have?

In thinking how to solve this problem, I reasoned that if I can solve it for slots, I can use that same solution for cutouts, holes, etc. In thinking about slots, I saw that I could easily specialize each case. That is, I'd have a Slot class and make a derivation for Slots when I had the V1 system and another derivation when I had a V2 system. I show this in Figure 4-1.

Figure 4-1. The design for slots.


I complete this solution by extending it for each of the feature types, as shown in Figure 4-2.

Figure 4-2. Original solution to the problem of extracting information.


Of course, Figure 4-2 is pretty high-level. Each of the V1xxx classes would communicate with the corresponding V1 library. Each of the V2xxx classes would communicate with the corresponding object in the V2 model.

This is easier to visualize by looking at each class individually.

  • V1Slot would be implemented by remembering the model it belongs to and its ID in the V1 system when it is instantiated. Then, whenever one of the V1Slot methods is called to get information about it, the method would have to call a sequence of subroutine calls in V1 to get that information.

  • V2Slot would be implemented in a similar fashion, except that, in this case, each V2Slot object would contain the slot object corresponding to it in the V2 system. Then, whenever the object was asked for information, it would simply pass this request on to the OOGSlot object and pass the response back to the client object that originally requested it.

A more detailed diagram incorporating the V1 and V2 systems is shown in Figure 4-3.

Figure 4-3. A first solution.


I am going to provide code examples for a couple of the classes in this design. These examples are just to help you understand how this design could be implemented. If you feel comfortable that you could implement this design, feel free to skip the following Java code examples (C++ code examples appear at the end of this chapter).

Example 4-1. Java Code Fragments: Instantiating the V1 Features
// segment of code that instantiates the features
// no error checking provided--for illustration
// purposes only

// each feature object needs to know the model number
// and feature ID it corresponds to in order to retrieve
// information when requested. Note how this information
// is passed into each object's constructor

   // open model
   V1Model modelNum= V1OpenModel( modelName);

   nElements = V1GetNumberofElements(modelNum);

   Feature features[]= new Feature[MAXFEATURES];

   // do for each feature in the model
   for (i= 0; i < nElements; i++) {
      // determine feature present and create
      // appropriate feature object
      switch( V1GetType( modelNum, i)) {
         case SLOT:
            features[i]=
               new V1Slot( modelNum,
                           V1GetID( modelNum, i));
            break;

         case HOLE:

            features[i]=
               new V1Hole( modelNum,
                           V1GetID( modelNum, i));
            break;

         ...

      }
   }

Example 4-2. Java Code Fragments: Implementation of V1 Methods
// modelNum and myID are private members containing
// information about the model and feature (in V1) this
// feature corresponds to

class V1Slot {
  double getX () {
    // call appropriate method for V1 to get needed
    // information. Note: this method may actually
    // call several methods in V1
    // to get the information.
    return V1GetXforSlot( modelNum, myID);
  }

class V1Hole {
  double getX () {
    // call appropriate method for V1 to get needed
    // information. Note: this method may actually
    // call several methods in V1
    // to get the information.
    return V1GetXforHole( modelNum, myID);
  }
}

Example 4-3. Java Code Fragments: Instantiating the V2 Features
// segment of code that instantiates the features
// no error checking provided--for illustration
// purposes only

// each feature object needs to know the feature in the
// V2 system it corresponds to in order to retrieve
// information when requested. Note how this information
// is passed into each object's constructor

   // open model
   V2Model myModel= V2OpenModel( modelName);

  nElements= myModel.getNumElements();
   Feature features[]= new Feature[MAXFEATURES];
   OOGFeature oogF;
   // do for each feature in the model
   for (i= 0; i < nElements; i++) {
      // determine feature present and create
      // appropriate feature object
      oogF= myModel.getElement(i);

      switch( oogF.myType()) {
         case SLOT:
            features[i]= new V2Slot( oogF);
            break;

         case HOLE:

            features[i]= new V2Hole( oogF);
            break;

         ...

      }
   }

Example 4-4. Java Code Fragments: Implementation of V2 Methods
// oogF is a reference to the feature object in V2 that
// the object containing it corresponds to

class V2Slot {
   double getX () {
      // call appropriate method on oogF to get needed
      // information.
      return oogF.getX();
   }
}

class V2Hole {
   double getX () {
      // call appropriate method on oogF to get needed
      // information.
      return oogF.getX();
   }
}

In Figure 4-3, I have added a few of the methods that are needed by the features. Note how they differ depending upon the type of feature. This means I do not have polymorphism across features. This is not a problem, however, since the expert system needs to know what type of feature it has anyway. This is because the expert system needs different kinds of information from different types of features.

This brings up the point that I am not so interested in polymorphism of the features. Rather, I need the ability to plug-and-play different CAD/CAM systems without changing the expert system.

What I am trying to do—handle multiple CAD/CAM versions transparently—gives me several clues that this solution is not a good one:

  • Redundancy amongst methods— I can easily imagine that the methods that are making calls to the V1 system will have many similarities between them. For example, the V1getX for Slot and V1getX for Hole will be very similar.

  • Messy— This is not always a good predictor, but it is another factor that reinforces my discomfort with the solution.

  • Tight coupling— This solution has tight coupling because the features are related to each other indirectly. These relationships manifest themselves as the likely need to modify all of the features if the following occurs:

    - A new CAD/CAM system is required.

    - An existing CAD/CAM system is modified.

  • Low cohesion— Cohesion is fairly low since methods to perform core functions are scattered amongst the classes.

However, my greatest concern comes from looking into the future. Imagine what will happen when the third version of the CAD/CAM system arrives. The combinatorial explosion will kill us! Look at the third row of the class diagram in Figure 4-3.

  • There are five types of features.

  • Each type of feature has a pair of classes, one for each CAD/CAM system.

  • When I get the third version, I will have groups of three, not groups of two.

  • Instead of ten classes, I will have fifteen.

This is certainly not a system I will have fun maintaining!

A pitfall of analysis: too much attention to details too early.

One common problem that we analysts can have is that we dive into the details too early in the development process. It is natural because it is easy to work with these details. Solutions for the details are usually apparent, but are not necessarily the best thing to start with. Delay as long as you can before you commit to the details.

In this case, I achieved one objective: a common API for feature information. Also, I defined my objects from a responsibility point of view. However, I did this at the price of creating special cases for everything. When I get new special cases, I will have to implement them as such. Hence, the high maintenance costs.


This was my first-blush solution and I immediately disliked it. My dislike grew more from my intuition than from the more logical reasons I gave above. I felt that there were problems.

In this case, I felt strongly that a better solution existed. Yet, two hours later, this was still the best I could come up with. The problem, it turned out, was my general approach, as will be seen later in this book.

Pay attention to your instincts.

Gut instinct is a surprisingly powerful indicator of the quality of a design. I suggest that developers learn to listen to their instincts.

By gut instinct, I mean the sensation in your stomach when you see something you do not like. I know this sounds unscientific (and it is), but my experience has shown me consistently that when I have an instinctive dislike for a design, a better one lies around the corner. Of course, there are sometimes several different corners nearby and I'm not always sure where the solution is.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.154.143