CPSC 333: Heuristics for Improving Designs

Location: [CPSC 333] [Listing by Topic] [Listing by Date] [Previous Topic] [Next Topic] Heuristics for Improving Designs


This material was covered during lectures on February 28, 1997.


Increasing the Level of Cohesion of a Module

For a ``utility module'' - one that's low down in the structure chart and actually does some work, rather than acting as a controller for other modules - consider factoring the module - splitting it into several modules that each performs a more ``cohesive'' subset of the original module's tasks, if the module's level of cohesion is too low.

This might also work for ``controller modules'' near the top of the structure chart. However, it's also possible that you can also make a controller module more cohesive by increasing its responsibilities.

To see that this is the case, note that the main module for the entire system is probably highly cohesive, since by saying that it ``controls the entire system,'' you can describe its responsibilities using a simple phrase. If you reduced its responsibilities - perhaps, by leaving it in charge of input, and complex computation, but handing the ``output control'' off to another module - you'd end up making the module a bit less cohesive than it was before. Turn this around, and you'll obtain an example of a module whose cohesion improves when it's given more to do.

It's possible to get carried away, in solving cohesion problems by creating numerous modules that each has a trivial job. If you do this, then you risk ending up with a system that's impossible to implement quickly or to maintain, because of the numerous modules and interfaces to support - adding each new module does incur an additional cost.

Reducing the Level of Coupling of a Set of Modules

If you actually found an instance of content coupling, then you should throw (at least part of) the design away, and start over. You haven't followed the rules that have been given here for producing an architectural design for a system.

After you've made sure that all the modules with common coupling really do need access to the data structure they share, and after you've ensured that modules with external coupling really need access to the same I/O device, you should create informational clusters for the data areas and I/O devices, providing controllable ``interfaces'' for these, as described above.

To reduce coupling when control coupling is found, check for tramp data, which is created by one module and passed from module to module, across a large part of the structure chart, until it's finally used to make a decision in another, and consider reorganizing the structure chart, so that if some module A does affect the behaviour of some other module B, then one calls the other directly (or as close to that as possible. Try to make sure that one of the modules being placed inside the scope of control of the other.

Stamp coupling and lower levels of coupling are completely acceptable. In general, try to make interfaces as simple as possible. An ``elementary'' parameter (with one of the standard data types) is generally preferable to a ``composite one;'' several elementary parameters probably aren't. Avoid using control flags or other mechanisms that will be difficult for later maintainers of the system to discover and to understand. Finally, avoid passing information unless that information is actually needed by the module receiving it.

Reducing the Size or Complexity of a Module

``Factoring'' an overly complex module - replacing it by several modules that are each shorter and simpler than the original - is a way of solving this problem, just as it's a way to reduce the level of cohesion of a utility module.

It's also possible that a module is complex because you've chosen a complex algorithm, when there's a simpler algorithm for the same problem that will be efficient enough for the system currently being developed.

For example, if you're implementing a sorting module, if you have a guarantee that you'll never be required to sort more than some small number (say, twenty or thirty) of values, and performance requirements aren't excessive, then you might reduce the complexity of a sorting module that implements ``quicksort'' by implementing an ``insertion sort'' instead.

(Note as well that if the number of elements to be sorted really is as small as I've said, then it's plausible that the insertion sort will also be at least as fast as quicksort when applied to solve the small problems the system is handling, simply because you need to deal with larger problems before the improvements associated with "quicksort" become apparent!)

Eliminating Decision Splitting

See the above information about reducing the level of coupling of modules when control coupling has been found.

Eliminating Redundant Modules

If you find two modules doing exactly the same thing, replace them by a single module that's called by more than one other.

If you find two modules that are doing almost the same thing, it might be worthwhile to replace these by a single module that is slightly more general than either of the original.

However, don't get carried away: If you go too far, you may end up with a single module that's too complex to maintain.

Eliminating Modules Whose Behaviour is Unpredictable

If a module is ``unpredictable,'' in the sense that you can't guarantee that it will do the same thing twice if it's called twice with the same inputs (and with the same values returned each time, by any modules that it calls) - then make sure that this really is necessary - that is, you're dealing with one of the ``exceptions'' described when predictability of modules was discussed.

If it isn't, then you should look for an algorithm for the problem that doesn't require the use of system ``state'' information, and that's deterministic.

Location: [CPSC 333] [Listing by Topic] [Listing by Date] [Previous Topic] [Next Topic] Heuristics for Improving Designs


Department of Computer Science
University of Calgary

Office: (403) 220-5073
Fax: (403) 284-4707

eberly@cpsc.ucalgary.ca