• No results found

Coupling is "the degree of interdependence between modules" [Fenton and Pfleeger 1997, p. 310; Yourdon and Constantine 1979]. As with cohesion, design theory says it is easier to modify and to reuse systems that minimize coupling. Yourdon and Constantine defined six kinds of coupling between two modules in an ordinal scale. This metric refers to the way in which a module uses data in another module, which (in a regular program) either happens through direct reference or by passing data on the stack.

1. No coupling: The two modules are totally independent of one another.

2. Data coupling: The two modules pass data (not control) through parameters on methods. 3. Stamp coupling: The two modules pass data of the same structured type through parameters

on methods (they both depend on a third structure).

4. Control coupling One module passes data that controls a decision point in the other module (control data, or flag parameters that a conditional statement checks before doing

something).

5. Common coupling: Both modules refer to shared global data.

6. Content coupling: One module refers directly to data inside the other.

Translating these ordinal values into the database world is reasonably straightforward. The database provides more connections, however. You can refer to data through associations, through generalization and inheritance, or by reference in operational code (SQL, OQL, or procedural code in stored procedures or methods).

No coupling in the database means that a given class does not have any associations to other classes and does not inherit from another class. Methods on the class do not refer to data in other classes in any way. They refer only by calling methods on other classes, executing stored global procedures that refer to data in other classes, or directly through SQL or variable references.

Data coupling limits access to persistent data values (not objects, just primitive data types) by encapsulating them all in methods. A class that accesses such data through method calls, but not by association or inheritance or direct reference and not any other object data, exhibits data coupling to the other class.

A class that gains access to structured data (record types, arrays, or objects) through methods exhibits stamp coupling. In a relational database, this means accessing data through stored procedures that return cursors or structured data (record types or some other structured object). In an OR database, stamp

coupling means having methods that pass in or return objects, such as the Oracle8 object view. In an OO database, stamp coupling can additionally mean using OQL constructs that refer to objects. For example, you can have expressions that return a set or bag of objects or expressions that use functions taking arguments of class types.

Control coupling is by definition procedural, not data oriented. To the extent a database language supports procedural control, you can have control coupling in your language statements. In the relational database, for example, a correlated subquery exhibits control coupling. This is where a nested SELECT refers outside itself to a value in the outer select (a parameter) to limit its return. Even more directly, creating a parameterized SQL statement that uses the parameter in a WHERE clause, or building an SQL statement using data passed in by parameter in a transient method, both exhibit control coupling. Boolean flags in methods are still the most common control coupling in OR and OO databases.

Turning this around, if you find yourself creating Boolean flags in your tables or classes, are you going to use these to make control decisions, or do they just represent true/false data? If it's the former, you should consider a subclass to represent the different control choice. For example, consider CriminalOrganization in the commonplace book. You can add a Boolean value to indicate whether the organization is active or not. If the intent is to represent information for query purposes, this is just fine. If the intent is to support a method that returns a different value if the caller sets the flag, that's control coupling. The same logic applies to invalid nulls—null values that represent not missing data but invalid attributes. Breaking the attributes out into a separate table eliminates the nulls and the control coupling they require.

Another way to look at this decision is to decide whether your flag is part of the object state or whether it represents a different kind of object. The best way to decide this is to think about what happens when the flag's value changes. Does the object need to mutate into a different kind of object? If it does, it's a strong indication that the flag represents state. Once you create an object, you generally shouldn't be converting it into another object because of some control transition in your system. It's better design to change the internal state of the object through an internal flag.

Common coupling refers to the use of global data. In a sense, using a database at all means you are using global data, and hence you raise your coupling level to common for any class that refers to data in the database. Global data is data that is visible to all parts of a system. To get access to data in a database, you connect to the database server, then use SQL, OQL, or some other access mechanism to manipulate the data. What little visibility control exists is part of the database security mechanism, not the

programming visibility mechanism.

Note

It would be interesting to design a feature into an RDBMS or ORDBMS to provide visibility control for just this reason. Your application could declare visibility of a particular schema, reducing your coupling to data/stamp coupling. I often use security mechanisms to achieve this effect by splitting up the integrated schema into subsystems stored in different areas (users, roles, databases, authorizations, or whatever the security construct in the particular DBMS). I then use security access mechanisms, usually either privilege grants or database links, to make data selectively visible. This limits the global impact, but it's not very portable and requires quite a bit of database administration on the server side. Another approach I've used is to encapsulate tables within stored procedures, or in the case of PL/SQL, with packages. I hide the tables in a user that owns everything but just grants access to the procedures, not to the underlying data.

With a relational schema, you don't have much choice but to access the data globally. OR schemas, unfortunately, do not encapsulate their data for the most part, so again you must access data globally. OO schemas, on the other hand, offer fully encapsulated access to data, so in an OO database you have full visibility control.

Similarly, with content coupling, you refer directly to data rather than referring to it only through methods. The relational and object-relational database do not encapsulate the data, and the SQL refers directly to it. You can use views and stored procedures to emulate encapsulation in relational and OR systems. The view is a way of encapsulating access to a schema by creating a stored SQL statement that looks like a table, at least for query purposes. Similarly, you can create stored procedures (in Oracle, stored packages) that emulate classes and methods with some limitations already discussed in Chapter 7. With OO

databases, you should never make your data visible directly but rather you should create accessor and mutator operations that provide access, thus reducing coupling to data/stamp coupling.

Coupling intimately relates to maintainability and reusability. The lower you are on the coupling scale, the easier it is to maintain your system, and the more likely you are to be able to reuse it under different scenarios. The less strongly coupled your classes are, the less impact do changes to any given class have on the rest of the system.

In OO design, there are three specific ways to limit coupling.

First, observe encapsulation. If you make your attributes public, you allow other classes to move all the way to content coupling, the worst variety. If you change your class, all the classes that use it must also change. The more you can conceal, the better. For example, do not just create get/set methods for all your attributes by rote. This is, strictly speaking, better than content coupling (it's data or stamp coupling), but you will find that changing the internal structure without changing the interface is quite difficult. Create an interface that is meaningful rather than just exposing your internal structure. A nice way to remember all this is to observe the Law of Demeter: any object should access directly only objects directly connected to it through association or generalization [Lieberherr, Holland, and Riel 1988].

In persistent terms, this means that you should encapsulate your data attributes as much as possible. If you use SQL or OQL in your coding, encapsulate it in the appropriate classes. SQL, almost by definition, is a global-variable programming language with no encapsulation at all. This leads naturally to a

programming style that uses common and content coupling. As just mentioned, there are ways around this using stored procedures and functions to encapsulate tables, and you can also use views to limit your exposure. Extended OR SQL is better, and OQL is better still, in that they give you more direct encapsulation techniques by integrating operations and methods into the programming language.

Second, use inheritance sparingly. If a subclass refers directly to a member in a superclass, that's content coupling. If you change the superclass, you have to change all the subclasses that use it. If you can, make your data members private and use superclass member functions to access superclass features.

Persistent inheritance is a bit more difficult to control. Avoid joins of superand subclasses where possible; instead, break access to the tables into their logical parts and place those parts into super-and subclasses in your transient code. Pass data from one query result into the query of the super-or subclass. This reduces the common coupling to data coupling. In OR and OO databases, use the private access declaration to limit access as much as possible.

Third, avoid control coupling by using polymorphism where possible and feasible. Control coupling is where you pass in data that you use to make some choice about the flow of control in your code. Passing in a Boolean flag is the most common example of this. You can usually avoid this kind of control by subclassing and defining virtual or polymorphic functions for the alternatives. There are certain circumstances, however, where this leads to difficulties.

For example, consider a system of currency conversion. You can create a class hierarchy with one class for each currency you support, adding conversion methods for each other currency and a common set of virtual methods for addition, subtraction, and the like. Using double-dispatching, you can construct arbitrary currency expressions that always return the right result no matter what currency you use where. In

operation, and in concept, this sounds great. In practice, you've traded one kind of coupling for another. The alternative to this is to have a single currency converter class that has a table of conversions. You pass in an amount and a currency flag (the control data), and you get back an amount in dollars or some other common currency. You can have another method in which you pass in dollars and a currency, and you get back the amount in the currency.

The second approach is definitely at the level of control coupling. The function uses the currency flag to decide where to look in its internal tables (or the database) for conversion factors. The first approach avoids this by distributing the table across a class hierarchy. However, now each class knows about each other class, which is minimally stamp coupling and maximally content coupling if you use friend access or some other direct mechanism to get at internal data. Stamp coupling is better than control coupling; however, in this case you have created a monster by creating dozens of classes all coupled together using stamp coupling. Add a new currency, and you literally must change all the classes in the currency system. The second approach, control coupling, is definitely preferable.

In the database, this translates to similar structures. A currency conversion table is preferable to spreading the conversion information among many separate tables, one for each currency. It would never occur to a relational database designer to think that way, for better or worse.