When Design Matters | CoreLinux++ | CoreLinux Consortium |
|
Standard 3: | Procedures shall return nothing. If the procedure fails, exit with an exception. |
Rationale: | If the precondition is satisfied, the procedure can only succeed. Any failure is, by definition, an exception. |
Standard 4: | Procedures that fail shall restore the object to the state that it was in upon entry. |
Rationale: | Minimize implicit design decisions. Engineers know that an object is unchanged if an exception is thrown. |
Standard 5: | Multiple sequential calls to a function, with the same parameters, shall return the same result. This is not applicable to a function that returns dynamic results, such as a iterator or cursor. |
Rationale: | A function should not change the state of an object, therefore the result should be reproducible. |
Guideline 5: | Functions should be const |
Rationale: | A function should not change the state of the object. There are cases where the function changes concrete class state, but does not affect the abstract state of a class. Bertrand Meyer has a discussion of concrete and abstract object states, see (Meyer, 1988). |
Guideline 6: | Functions that fail should throw an exception. |
Rationale: | If the function fails it must be an exception. There may be some scenarios where the standard exception mechanism grossly complicates the design or the code, in which case returning a status is acceptable. |
Guideline 7: | Functions that must return error values should separate the error values from the valid return types. |
Rationale: | Separating error return values from valid return data adds to the clarity of the code. CARE MUST BE TAKEN NOT TO ABUSE THIS MECHANISM. Functions returning error status should still check pre and post conditions. |
This metaphor emphasizes the fact that each member of the interface presents some necessary attribute, or operation, of an abstraction. A large number of dependencies between member functions are indicative of a procedural design. The goal of designing a class interface is that each public function represents a single, complete, and independent component of the abstraction. This applies to the public, as well as the protected interfaces.
Guideline 8: | Member functions should not have to be called in any particular order. |
Guideline 9: | No initialization should be needed after construction. |
Rationale: | Functional dependencies shall be clearly documented in the design, as well as in the class header. |
The traditional method of handling errors, namely passing error codes up the return stack, is a major source of code complexity. It is also a source of undesirable "control coupling" between modules. In C++ certain operations, such as construction, destruction, and assignment, do not allow for any value to be returned. This results in several different error handling schemes, depending upon what operations are involved.
The exception-based error handling model provides a well defined, robust, and extensible mechanism in which to handle errors. It provides a clean separation between the normal execution path and the error recovery code, resulting in a marked reduction of code complexity.
Standard 6: |
All exception must derive from the base class: Exception .
|
Rationale: | The undisciplined use of exceptions leads to code that is less robust, and harder to maintain. By deriving from a common base class, most code needs a single catch block, i.e. catch(ExceptionRef) |
Standard 7: | If the catch block does not correct the error, it must re-throw the exception. |
Standard 8: |
Any procedure that can change the state of the object, must restore the original state in the
event of an exception.
This requires that an internal unwind to the invariant state is the responsibility of the procedure prior to throwing the exception. |
Standard 9: | Every thread must have a try/catch block at the outermost level. |
Standard 10: | Assertion exceptions will always terminate the program. |
Standard 11: | Exceptions generated by a class shall be clearly documented in the design, as well as the class header. |
Rationale: | Exceptions are part of the interface of a class. They must be clearly documented as the rest of the class interface. |
Guideline 10: | Do not use exception handling for flow control.. |
Guideline 11: | Catch blocks shall not return if they are unable to correct the problem. |
Rationale: | Functions and procedures can only succeed or fail. Failure is signaled by throwing an exception. Catch blocks that do not correct the problem must not return as if the function were successful. This is a guideline because exception handlers at the topmost level (i.e. the user interface) may simply report the exception to the user and allow the program to continue running. |
Guideline 12: | Create as few new exceptions as possible. |
Rationale: | Each exception class adds complexity to the system. Most exceptional cases are detected by the use of invariants and assertions, or via system exceptions. |
Guideline 13: | All function calls should be in the context of some class. |
All classes shall explicitly code a default constructor, a copy constructor, a destructor, and an assignment operator. Minimize implicit design decisions. By declaring and implementing the automatically generated members, clearly the engineer has taken them into account and thought through their implications. The required members shall be private if the class semantics dictate that they are not needed by the class.
Guideline 14: | The use of friend functions is discouraged. |
Rationale: | Friends can usually be replaced by some other mechanism. For example, an iterator can be a friend or it can be an embedded class. If alternate mechanisms that do not excessively complicate the design are available, then use those. |
Standard 12: | Friend classes shall not be used. |
Rationale: | If every member must have access to the implementation of some other class then the friend class should be embedded. A class that must be a friend to more than once class indicates a design problem. |
Guideline 15: | The use of virtual inheritance is discouraged. |
Standard 13: | Name spaces are now widely supported by C++ compilers and should be used. |
Standard 14: | All "global" information shall be partitioned with namespaces. |
Run time type checking are now widely supported by C++ compilers and should be used.
The use of streams is dependent upon the nature of the project. If the project is a graphical application, the stream metaphor does not fit well, and should not be used. Small applications, tools, and utilities, may use streams. For example, a pretty printer or code formatter should use streams. If the choice is between streams or printf() functions, streams should always be chosen.
The use of streams in graphical applications shall be limited to output of error log and debug information. Streams shall always be used in place of the older 'C' stdio libraries. Streams are type safe. They can also be extended to support new types as they are added.
TO BE DONE
TO BE DONE
Guideline 16: | Do not use static objects. |
Rationale: | There are two well known problems with C++ static objects. The first is with the order of execution of static object constructors, and initialization dependencies between static objects. The second has to do with throwing exceptions, where does the enclosing catch block go? |
Standard 15: | Classes shall be self initializing |
Rationale: | Initialization is a detail of the implementation. Should an alternative implementation be found, that doesn't require initialization, clients will have to change. If the client is responsible for initialization, then the client must be aware of order if initialization issues. If the class initializes itself, then there is no problem with order of initialization. |
This consortium is only concerned with portability between ports of the Linux system.
Standard 16: | To enhance portability the CoreLinux++ library includes type wrappers that may need conditional compilation directives. The CoreLinux++ libraries should used the types defined in Types.hpp. |
Guideline 17: |
Native C++ classes ( streams, stl, string ) should be used but it is recommended that we at
least wrap them. For example:
class String : public string |
Rationale: | We recognize that the standards do not provide all the real world abstractions that are generally useful in most development scenarios. In our goal to provide this extended functionality it may be that we want to exploit what the standard provides and still have the means to extend it to fit the real world needs. |
Guideline 18: | Generic types should be implemented using templates. |
Rationale: | Keep the benefits of strong static type checking. |
Guideline 19: | Consider breaking a template into a non-template base class for non-type specific functions and a template for the type specific functions, especially if the template will be instantiated many times, or if there are a large number of non-type dependent member functions. |
Rationale: | Reduce the amount of redundant code that is generated. |
Design for clarity and ease of understanding instead of focusing upon performance. Donald Knuth advises use to "first create solutions using excellent software engineering techniques, then, only if necessary, introduce small violations of good software engineering for efficiency", see (Knuth, 1974).
The engineer must know what tools and components are available, and when they should be used. The selection of correct data structures and algorithms will boost performance more than attempting to optimize the implementation of inappropriate ones.
Guideline 20: | Coding in assembler is the optimization technique of last resort. |
Standard 17: | If there is not empirical proof that a piece of code is a bottleneck it does not need to be optimized. |
Rationale: | In most situations optimizing small portions of code result in huge performance gains. It is very difficult for the engineer to determine which sections of code are the best candidates for optimization. Use of a profiler on the other hand tells the engineer exactly where performance bottlenecks are. |
We should be using Rational Rose for analysis and design. This tool implements the Booch design methodology, and accompanying notation. Engineers are expected to be completely familiar with the methodology and the notation. All aspects of the methodology will be used, with the exception of module and process diagrams. A full treatment of the methodology can be found in Booch (1994). Dated, although RR is useful and includes UML support, it is not available on the Linux platform. Until we agree on a tool for Linux we will hand code UML.
- contact the webmaster |
|
SourceForge.net |
NoMagic, inc.
thanks Gary! |
|