Module Dependences in Software Design
Total Page:16
File Type:pdf, Size:1020Kb
Module Dependences in Software Design Daniel Jackson MIT Lab for Computer Science 200 Technology Square Cambridge, Massachusetts 02139 [email protected] Abstract A new model of inter-module dependences is proposed. ! e key idea is that dependences are mediated by specifi cations, so that not only the existence of a dependence is recorded, but also its quality. A single module does not necessarily o" er only a single specifi cation; each dependent module may use it through a di" er- ent specifi cation. ! is notion of dependence seems to explain some common programming idioms more readily than the conventional notion, and o" ers new opportunities for analysis and design cri- tique. 1 Introduction Software designers have long recognized the signifi c ance of dependences between modules in evaluating a design. When modules have many interdependences, the system is harder to understand; there is less fl e xibility to divide the labour of coding; and local changes have wide repercussions. But despite the seminal role of depen- dences, most designers have only a fuzzy sense of what they are and how to express them. Although various kinds of code dependences have been heavily investigated because of their role in separate compilation and program analysis, much less at- tention has been paid to design dependences. As a result, dependence diagrams are much less useful than they might be in program design, and cannot be used as a basis for analysis. ! i s paper outlines a new dependence model. Work on the model is in its pre- liminary stages, but it does seem to resolve some of the problems of the standard model, and in particular accounts better for some common idioms in object-ori- ented designs. 2 ! e Standard Model and its Defi ciencies ! e standard notion of module dependence, fi r st articulated by Parnas [8], is famil- iar to most developers. ! e system is described as a graph whose nodes represent modules and whose edges represent syntactic dependences. A dependence of mod- ule A on module B, drawn as an arrow from A to B, says that A, in providing services to the modules that depend on it, makes use of B. More precisely, A depends on B monterey.indd 1 8/25/03, 5:56:49 PM or uses B if “correct execution of B may be necessary for A to complete the task de- scribed in its specifi c ation” [8]. In teaching software engineering to undergraduates over the last few years, we have used these ‘module dependence diagrams’ to explain software designs, and have required students to produce them as documentation [6]. But we have come to realize that these diagrams are inadequate, and often do not capture the insights that motivate a design: á Transitivity. ! e dependency relation is, by defi nition, transitive, when intuitively it should not be. If A uses B and B uses C, then, in the absence of additional infor- mation, we must assume that any change to C a" ects B, and thus indirectly A, and thus A uses C. But perhaps B was inserted between A and C precisely to shield A from changes to C, and most changes to C will not a" ect A. á Polymorphism. Crucial elements of modern languages, such as polymorphism and interfaces, are not accommodated. Consider a typical Java program in which a client class C makes use of a hashtable class H and a key class K. Does H depend on K? On the one hand, the code of H makes no reference to the code of K; H likely belongs to a library that was written before K. On the other hand, changes to K can certainly cause H to fail. If the equals and hashCode methods of K are incom- patible, for example, a crucial representation invariant of the hashtable will be violated (that equal keys are kept in the same bucket chains), and a lookup with a given key may not return the value last inserted under that key. á Preconditions. Presenting a module with a bad procedure argument can cause it to fail. If a module’s specifi cation imposes a precondition, the designer’s intent was that blame for a bad input should be attributed to the calling module. But a module cannot easily be said to ‘use’ the modules that call it. á Replacement. Since a dependence of A on B does not indicate which properties of B are actually used by A, one cannot tell what properties of B should be preserved in a module that replaces it. ! e dependence diagram cannot therefore be used to reason about compatibility of components. 3 A New Model Our new model is based on two simple ideas. First, a dependence represents an assumption: a module A depends on a module B if the designer or implementer of A makes an assumption about the environment in which A is to be inserted that is justifi ed in the constructed system by the presence of B. ! e assumption may be that A is executed with a certain frequency; or that values passed to it satisfy certain con- ditions; or that values returned to it by procedures it calls are related to the values it passed them in certain ways. Second, dependences are mediated by specifi cations: one module does not de- pend directly on another, but rather on a specifi c ation which the other module must satisfy. A dependence of module A on module B mediated by specifi c ation S means that A assumes the conditions guaranteed by S, which the system is confi gured to provide through B. monterey.indd 2 8/25/03, 5:56:50 PM e se notions address some but not all of the defi ciencies of the standard mod- el: á e re is no transitivity problem, because of the interposed specifi cations. If A depends on B via specifi c ation S, and B depends on C via specifi c ation T, it is clear that changes to C that preserve the behaviour required by T will not a! ect B, and of the changes that a! ect B, only those that cause B no longer to satisfy S will af- fect A. á e use of polymorphic modules is clarifi ed. A hashtable H depends on its key class K via the specifi c ation Object Ð what in Java is referred to as ‘the object con- tract’. e dependence of H on K is an artifact of the confi guration: in this case due to some client class C passing objects of type K to the hashtable class H. e designer of H is not expected to predict the use of K, but can assume that any class whose objects are used as keys will satisfy Object. á A precondition is represented by a dependence in the reverse direction; the pre- and postconditions of a procecure specifi c ation can be regarded as distinct specifi cations associated with data fl o ws in di! erent directions. á Reasoning about replacement comes easily as a byproduct of the use of specifi c a- tions. A dependence of a module that makes the assumption described in speci- fi c ation S will be preserved appropriately if the module it depends on is replaced by one that also satisfi es S. If specifi c ations are treated as uninterpreted names (which will often be convenient), an explicit ordering on specifi c ations can be used to justify replacements that do not involve exact matches. Specifi cations appear, at least implicitly, in the traditional model, but they play a di! erent role. In Parnas’s formulation, the specifi c ation of the using module, and not the used module is relevant. Furthermore, in our model, specifi cations are associ- ated with dependences rather than modules. A single module may have many speci- fi c ations, each representing a view of the module presented to another module that depends on it. In the hashtable example, key class K appears to the hashtable class H under the specifi c ation Object, but will likely appear to the client class C under a stronger specifi cation that provides domain-specifi c properties. 4 Example: Observer Many of the ‘Gang of Four’ design patterns [3] are motivated by dependence consid- erations. We illustrate our dependence model on the Observer pattern. e fi gure shows dependence diagrams for a fragment of a program that has been refactored with Observer. e original program on the left has a subject class Subj and a concrete observer class CObs. Updates to Subj from a client class Client are propagated to CObs by calls to procedures specifi c to CObs. We have shown this by labelling the dependence with a specifi c ation CObs: using the name of the module for the specifi c ation is intended to suggest that the specifi c ation promises all the properties the module is designed to provide. e refactored program on the right di! ers primarily in the specifi c ations that mediate the dependences. e key di! erence is that Subj no longer interacts with monterey.indd 3 8/25/03, 5:56:52 PM Client Client Update Subj Reg ConsObs Obs Subj CObs CObs Subj CObs Get CObs via the specifi c ation CObs, but rather through a far weaker specifi cation Obs, a generic observer specifi c ation that could be used for other observer classes. ! is is the central motivation of Observer: it allows new observer classes to be added without changing the code of Subj.