Coupling and cohesion
As Example 8 shows, developers need to deal with the dependencies that arise as a result of their decomposition of a problem and its solution into a number of modules. We say that a module of a system depends on another if it is possible that a change to one module requires a change to another. For example, if a business changes its production methods this may cause a consequent change in the way it calculates the payments required for the goods it produces.
A developer must not only deal with the nature of each dependency but also the number of dependencies. In software engineering, ‘coupling’ is used to refer to the degree of interdependence among the different parts of a system. It is easy to see that certain systems can have chains of interdependent modules where, for example, module A depends on module B, which depends on module C, and so on. In some cases these chains may join up and create a circular dependency, which is a particular form of strong (or high) coupling.
Developers try to construct loosely coupled systems because they are easier to understand and maintain. So a good software system has low coupling, which means that changes to one part are less likely to propagate through the rest of the system. A further benefit of low coupling is that components are easy to replace and, potentially, reuse.
Whatever the level of coupling in a software system, it is important to know which modules are coupled. If there were no records of the coupling between modules, a developer would have to spend time working through the modules to determine whether or not each was affected by a change. The result would be a lot of effort spent on checking, even if no changes were needed. Example 9 illustrates the danger of having more than one module making use of common or shared data.
Date handling has always been a problem for software developers. For applications of a certain age, the most applicable storage format for representing a year was a number between 0 and 99. It made sense because 1966 was stored as 66, 1989 as 89, and so on, therefore less space was needed to store just two digits. Furthermore, if dates were stored as numbers, tasks that involved sorting by date order were simple – 22 January 1989 stored as 890122, is after 22 December 1966 stored as 661222.
Unfortunately, a number of these applications were still in use as the year 2000 approached, so every module in every application that used the short form of year had to be investigated.
A major aspect of the problem in Example 9 was that different developers had different ways of reading and manipulating the values stored in variables that used the six-figure date format. This increased the effort required to resolve the so-called millennium bug. If developers had had a consistent way to manipulate dates that did not rely upon the storage format, the millennium bug would not have been an issue of concern.
Cohesion is a way of describing how closely the activities within a single module are related to each other. Cohesion is a general concept – for example, a department in an organisation might have a cohesive set of responsibilities (accounts, say), or not (miscellaneous services). In software systems, a highly cohesive module performs one task or achieves a single objective – ‘do one thing and do it well’ is a useful motto to apply. A module should implement a single logical task or a single logical entity.
Low coupling and high cohesion are competing goals. If every module does only one thing at a low level of abstraction, we might need a complex edifice of highly coupled modules to perform an activity at higher levels of abstraction. A developer should try to achieve the best balance between the levels of coupling and cohesion for a software system. For example, hotels generate income by letting out their rooms to guests. The concept of room is likely to be represented somewhere in the software system for reservations for a hotel. It may be convenient to use a module or class representing the concept of room to collect and store data about the income generated by letting rooms. However, a better solution is to have a separate bill or payment module, because it is more cohesive, especially when a hotel generates income in other ways, for example, from serving meals to people who are not resident guests.
Activity 5 Divide and conquer
- a.Why might you consider splitting up a large project into smaller chunks?
- b.How does the complexity of a software system affect the maintenance task?
- c.What is a module?
- d.Why does it help to have low coupling in a software system?
- e.Give examples of the kinds of information that would be valuable when considering a change to a given module.
- f.What are the context dependencies of a module? How do they relate to a module’s interface?
- g.What are the benefits of using modules with defined interfaces?
- h.Why does it help to have high cohesion in the modules of a software system?
- i.What characteristics should a module display that will help to ensure that it is easy and cheap to develop and maintain, and that errors are kept to a minimum?
- j.Why is it important to achieve a balance between coupling and cohesion?
- a.There is a limit to how much one person can understand at any one time. So there is a limit to the size of a software system that any one person can deal with. By splitting a large project into smaller chunks, it is possible to identify a number of more manageable tasks for those involved.
- b.It is essential to be able to make a change to a software system without having to know all about that system. Each change becomes difficult when the flow of control and dependencies within programs are complex. The greater the number and nature of the dependencies, the harder it is to maintain a software system.
- c.A module is any identifiable part of a software system that is considered separately. For example, modules may be subroutines (in a procedural language equivalent to methods), classes (in an object-oriented language), library functions or other constructs that may be treated independently.
- d.With low coupling, there are few dependencies between modules. Therefore changes made to one part (one or more modules) of a software system are less likely to propagate throughout the whole system. (A clear record of the dependencies between modules helps you to predict the impact of a proposed change to a software system.)
- e.There are two kinds of information that contribute to the analysis of a proposed change:
- Which modules are clients of the module in question? This information indicates how far a change may propagate through the software system.
- What assumptions have been made in client modules of the module in question? An understanding of the expected services of a module will help assess the risks associated with a particular change.
- f.The context dependencies for a module are the services of other modules that the module needs in order to work correctly. You can express the context dependencies for a module in terms of other interfaces. In effect, you can express the responsibilities of a module in terms of its interface and context dependencies. If the context provides the services that the module needs and clients meet any conditions specified in the interface, the module can guarantee the provision of the services described in its interface.
- g.The benefits are as follows:
- Developers will need to know only about the module’s interface (its syntax and what it requires and achieves – its semantics), not how it provides those services. Consequently developers can be more productive.
- Developers can understand aspects of the software system more thoroughly, so fewer bugs will be introduced.
- It should be easier to find bugs, as irrelevant modules are avoided.
- The possibility of module reuse is increased once it is known what that module provides and requires.
- h.With high cohesion, a module carries out a sensible set of operations or activities. Ideally high cohesion implies just one major abstraction per module. The interface abstracts away from what a developer must know in order to use a module. This makes it easier for developers to understand the purpose of the module and how to use it. In addition high cohesion tends to make a module more reusable in other applications, because it provides a set of operations that sit naturally together.
- i.A module should have low coupling and high cohesion, represent a good abstraction, and have a well-defined interface that is an encapsulated abstraction of a well-understood concept.
- j.In constructing a system, you may have a choice between a smaller set of loosely coupled, less cohesive modules, or a larger set of tightly coupled, more cohesive modules. In the former case each module may be difficult to understand, while in the latter case the relationships between them may be over-complex. You need to strike an appropriate balance.