Next: Separating Parser from
Up: Modularity Principle
Previous: Modularity Principle
The primary criterion for deciding whether two things go in the same module (class) is whether they must share knowledge about how a piece of data is represented.
A symbol table (ST), for example, can be represented in many ways: linked list, array, hash table, tree, etc. However, clients of the ST need not and should not know how the ST is represented. The ST may be used by every stage of the compiler. You want to design your ST so that changing it from an array to a list will require changing only the ST module. Otherwise, you will have to change most of your compiler, just to tweak your ST. That's unnecessarily expensive and difficult. The goal then is to isolate the ST representation design decisions from the rest of the compiler.
What we do, then, is hide the ST representation ``behind'' a set of functions (insert-elem, delete-elem, gettype, openscope, closescope, new ...). Each of these functions knows how ST is represented, but no other functions do. If you define your ST with C++ and have no public data members, then you have at least in part encapsulated your ST representation. However, if your ST is implemented with a linked list and you have member functions such as next and prev to get the next and previous ST element, then you may in fact be unwittingly ``exposing'' the linked list implementation to the outside. Such functions would only be a problem if you had troubles reimplementing them on another representation, such as an array.
Likewise, you want it to be easy to add function to the ST as new features are required. These additions should not affect the existing interface, otherwise, existing clients are effected, violating the goal of changing only the ST.