Framework-Based Software Development in C++
Gregory Rogers

Introduction

Framework-based software development requires a different approach to solving problems. It's not really top-down or bottom-up, it's sort of inside-out. You start by studying your current problem. Then you compare it to other problems and determine those aspects of your problem that are unique. Those aspects that are not unique are built into frameworks. The frameworks are then tied together and extended for a specific problem.

The methodology is very dependent on a set of emerging standards that, when used together, comprise what is described as an object infrastructure. These standards are:

It is a bit complicated to combine these standards presently because the products that implement them have evolved from different lineages.

A catalog of design patterns is presented. The catalog is fairly small and its patterns are geared toward framework development using C++, CORBA, and ODMG-93. The applicability and depth of discussion of each does not compare with the Gang-of-Four catalog.

Fourteen design rules are also included. Many originated in various textbooks and articles but now exist as popular styles and idioms.
  1. Design base classes to be "inheritance friendly" by making data members and functions protected instead of private, unless you have a good reason to do otherwise.
  2. Never return error conditions from function calls. Use exceptions for all error reporting.
  3. Name each persistent object, allowing anyone to get a reference to that object by name.
  4. Make arguments to functions string or d_String instead of const char* to avoid core dumps due to dereferencing NULL.
  5. Use const char* instead of char* whenever possible.
  6. Use the following contract between caller and callee: if non-const pointer is a return value, the caller is responsible for deleting the space pointed to when finished using the return value.
  7. To return a single small value, return it by value as the return value of the function. To return a single large value, fill in a non-const reference to an object passed as an argument by the caller.
  8. For transitive operators, always return *this.
  9. If a function argument is not of a built-in type, it should be passed by reference.
  10. Never add a member function whose purpose can be satisfied with a combination of other member functions and trivial logic.
  11. Assign a namespace to every framework.
  12. Don't build frameworks that have any dependency on a specific GUI API.
  13. Don't build a framework that relies on nonstandard components or tools, unless you have the source code for it.
  14. Use the ORB sparingly, preferably for control information being passed between coarse-grained objects or passing small amounts of simple data.

Domain analysis

  1. Categorize the problem.
  2. Research similar problems.
  3. Prepare a domain description.
    1. Create list of characteristics describing problem domain.
    2. Reword characteristics to be more like requirements.
    3. Separate domain-specific characteristics from generic ones.
    4. Graphically partition domain-specific characteristics from generic ones.
    5. Create new generic characteristics.
    6. Identify facets by graphically clustering the generic characteristics.
    7. Create separate lists of characteristics for each cluster and show existing assets from the repository.
  4. Analyze generic facets of the domain.
  5. Repeatedly review and refine the analysis.

Designing a framework

  1. Identify applicable design patterns.
  2. Map framework classes to roles. Decide on the names of classes in the framework that will play the roles in the patterns. Also, name data members and member functions that play roles in patterns.
  3. Introduce new classes. Round out the remaining design with entities in the domain that have not been accounted for in the roles of the selected patterns.
  4. Decide how the framework will be extended. Consider the various techniques that can be used to implement abstract classes:
  5. Design class interfaces. Map out division of labor across all the classes. Define relationships. Incrementally specify each class's public and protected sections.
  6. Design primary usage scenarios. Document the timing-oriented behavior with Object Interaction Diagrams.
  7. Rework class declarations. Tune the data members and member functions in response to the insight gained from the usage scenarios.
  8. Apply the design rules.
  9. Work through an example usage. Go back to the original problem that inspired the domain analysis and framework and determine how you would extend the framework to solve that problem.
  10. Document the design. While the discovery of the design may emerge from stream of consciousness and exploration activities, the presentation of the design must take a logical, top-down approach.
  11. Apply and document design metrics.
  12. Hold a design review.
  13. Incorporate comments from the review.

Implementing a framework

  1. Document source code packaging strategy (e.g. one header file and one source file per class).
  2. Set up a source code control environment.
  3. Pseudocode member function implementations.
  4. Set up a build environment.
  5. Code and compile the member function bodies.
  6. Prepare manual pages.