CSC/ECE 517 Fall 2007/wiki2 7 an: Difference between revisions
Line 155: | Line 155: | ||
== References == | == References == | ||
[http://www.amazon.com/Software-Engineering-Roger-S-Pressman/dp/0077096770/ref=pd_bbs_1/104-5343938-0067942?ie=UTF8&s=books&qid=1193191681&sr=8-1 Software Engineering,A Practitioner's Approach,Roger.S.Pressman] | [http://www.amazon.com/Software-Engineering-Roger-S-Pressman/dp/0077096770/ref=pd_bbs_1/104-5343938-0067942?ie=UTF8&s=books&qid=1193191681&sr=8-1 Software Engineering,A Practitioner's Approach,Roger.S.Pressman] | ||
[http://www.amazon.com/Object-Oriented-Analysis-Design-Applications-3rd/dp/020189551X/ref=sr_1_1/104-5343938-0067942?ie=UTF8&s=books&qid=1193191919&sr=1-1 Object Oriented Analysis and Design, Grady Booch] | |||
== External Links == | == External Links == |
Revision as of 02:13, 24 October 2007
Cohesion and coupling. Cohesion and coupling are concepts that are reasonably easy to understand, but nonetheless, it is challenging to find good examples that are succinct. Browse the hundreds of Web pages that attempt to explain these concepts, picking your favorite examples. Many of these pages mention related concepts; list some of them and explain how they relate to cohesion and coupling. Be sure to mention the metrics that can be used to measure how well a program conforms to the principles of high cohesion and low coupling.
What is coupling?
Coupling is the degree to which each program module relies on each one of the other modules. Unnecessary object coupling needlessly decreases the reusability of the coupled objects and also increases the chances of system corruption when changes are made to one or more of the coupled objects.
Thus the design goal is to minimize coupling and improve reusability. Coupling is broadly divided into two types-low coupling and high coupling. Low coupling refers to a relationship in which one module interacts with another module through a stable interface and does not need to be concerned with the other module's internal implementation With low coupling, a change in one module will not require a change in the implementation of another module. Low coupling is often a sign of a well-structured computer system, and when combined with high cohesion, supports the general goals of high readability and maintainability.
On the other hand, systems with high coupling face the following problems
• Change in one module forces a ripple of changes in other modules.
• Modules are difficult to understand in isolation.
• Modules are difficult to reuse or test because dependent modules must be included.
Thus it is evident that low coupling tends to produce reusable methods. But it is also not possible to write completely decoupled methods since the program will not work.
Examples of coupling
High coupling
A perfect example of high coupling is the human body. Every organ can be considered as an independent module which is intricately dependent on many other organs for its functioning. An irregularity in the working or performance of any organ affects almost the entire body. Thus the modules can be considered to be tightly coupled in this case. A direct consequence of this high coupling is that reuse of modules is extremely difficult. Imagine doing a liver transplant without having to worry about organ rejection!!! This is unfortunately not possible due to the highly coupled nature of the human body.
Low coupling
An example of a low coupled system is the modern plumbing system. Almost every part of the system can be replaced with ease since the modules hardly depend on each other.
Types of coupling
Content coupling
In this type of coupling, one module modifies or relies on the working of another module. Therefore any change in the second module leads to a change in the working of the dependent module. An example of this type of coupling would be case in which one module refers to some data in another module by a fixed offset. Thus as the data in the module changes the value accessed by the dependent module also changes.
Content coupling is bad and should be avoided. The reasons are
• Changes in the called module may require corresponding changes in the calling module too.
• Since the two modules are inextricably linked, it is not possible too reuse the calling module without including the called module.
Common coupling
In this type of coupling, two modules share the same global data. Here again, changing one module requires changing the other. Reusability is also reduced. There are also security concerns in the sense that a malicious module may adversely manipulate global data which can affect the entire design. Coupling between modules c, g and k represents this type of coupling.
Control coupling
As the name suggests, one module can control the decisions made by another module. This is usually done by passing the controlling information from one module to another. An example would be a module that prints a name in either upper or lower case, the decision of which is taken by another module which passes a control flag (0 or 1) to the first module. For this type of coupling to work efficiently, one module must know the internal logic and structure of another module. Coupling between modules d and e represents this type of coupling.
Stamp coupling
This type of coupling is also known as data structured coupling since modules share a composite data structure but use only a part of it. Thus it becomes harder to understand what the individual modules do and makes it easier for the modules to accidentally mess up with the data. This can also lead to malicious data modification. An example would be passing a partially used data structure between modules by means of a pointer in C. Coupling between modules b and a represents this type of coupling.
Data coupling
This is the best type of coupling in which modules share data through parameters. As a result, modules only communicate data which is used between them. Thus changes in one module are less likely to affect the other module, leading to better maintenance. Coupling between modules a and c represents this type of coupling.
What is cohesion?
Cohesion is the measure of the strength of functional relatedness of elements within a module. Cohesion can also be defined as the degree of interaction within a module since it describes how well the contents of a module cohere. A cohesive module performs a single task within a software procedure, requiring little interaction with procedures being performed in other parts of a program. Stated simply, a cohesive module should (ideally) do just one thing. Cohesion is also broadly divided into two types- high cohesion and low cohesion. Modules with high cohesion are desirable because they support code reuse, maintenance and design change. Modules with low cohesion on the other hand, are difficult to test, maintain, reuse and even difficult to understand.
Examples of cohesion
Low cohesion
Consider a temperature measurement system for a piece of machinery. The concerned module has the following tasks
• Initiate periodic measurement of temperature
• Compare measured temperature with optimum values
• Produce an error report in case of discrepancy and subsequently notifies the user
• Update the report in the database
• Take necessary action like turning on the cooling system etc
Although the above tasks are loosely related, each is an independent functional entity that might best be performed as a separate module. Combining the functions into a single module can serve only to increase the likelihood of error propagation when a modification is made to one of its processing tasks.
High cohesion
An example of high cohesion is a module which is dedicated to perform just one task. In the above example, if each of the functional entities are served by an independent module, the system would be described as a highly cohesive system.
Types of cohesion
Coincidental cohesion
This is the worst type of cohesion and should be avoided. Coincidental cohesion is when the activities of a module are related neither by data nor by flow of control. It may seem like the parts of a module have no significant relationship and are randomly grouped. An example would be a module which multiplies two numbers and prints out the string hello! It has low reusability, low maintainability and in order to fix a module it is necessary to break it up into smaller, independent modules.
Logical cohesion
A logically cohesive module contains a number of activities of the same general kind. Thus parts of a module are grouped because they logically are categorized to do the same thing, even if they are different by nature. The activities, although different, are forced to share the one and only interface to the module. The resulting module is neither easy to understand nor to maintain.
Temporal cohesion
A temporally cohesive module is one whose elements are involved in activities that are related in time. Temporally cohesive modules, like procedurally cohesive ones, tend to be composed of partial functions whose only relationship to one another is that they all happen to be carried out at a certain time. The activities are usually more closely related to activities in other modules than they are to one another, a situation that leads to tight coupling.
Procedural cohesion
Procedural cohesion is when parts of a module are grouped because they always follow a certain sequence of execution. They are composed of pieces of functions that have little relationship to one another except that they’re carried out in a specific order at a certain time. An example would be a module which accepts start and destination addresses and then calculates the shortest route between them.
Communicational cohesion
Communicational cohesion is when parts of a module are grouped because they operate on the same data. For example to find out some facts about a book we may wish to fond the title of the book, its author, price and publisher. These four activities operate on the same input data which makes the module communicationally cohesive.
Sequential cohesion
A sequentially cohesive module is one whose elements are involved in activities such that output data from one activity serves as input data to the next. Its structure is thus similar to an assembly line. An example would be of a module which reads data form a file, processes the data and displays the appropriate output.
Functional cohesion
This is the best type of cohesion with respect to maintainability. A functionally cohesive module contains elements that all contribute to the execution of one and only one problem-related task. An example would be a module which is dedicated to computing the cosine of a given angle
Cohesion v/s Coupling
As discussed earlier, cohesion is a measure of the relative functional strength of a module while coupling is a measure of the relative interdependence among modules. Coupling is usually contrasted with cohesion; high cohesion is associated with loose coupling, and vice versa. Modules with high cohesion tend to be preferable because high cohesion is associated with several desirable traits of software including robustness, reliability, reusability, and understandability whereas low cohesion is associated with undesirable traits such as being difficult to maintain, difficult to test, difficult to reuse, and even difficult to understand. Thus high cohesion and low coupling are desired design considerations. The reasons are
• Modules with high cohesion and low coupling support code reuse
• Such modules are easy to maintain
• They support design change.
High cohesion can be achieved by using good kinds of cohesion like functional cohesion and coupling can be reduced by using good kinds of coupling like data coupling.
Metrics to measure cohesion and coupling
A small number of metrics have been proposed to measure cohesion and coupling in object-oriented systems. Chidamber and Kemerer proposed a set of six object-oriented design metrics based on measurement theory. These include Depth of Inheritance Tree (DIT), Number of Children (NOC), Coupling between objects (CBO), Response for a Class (RFC), Weighted Methods per Class (WMC), and the Lack of Cohesion in Methods (LCOM) metrics.
Depth of Inheritance Tree
DIT = maximum inheritance path from the class to the root class.
The deeper a class is in the hierarchy, the more methods it is likely to inherit, making it more complex. Deep trees as such indicate greater design complexity. As a positive factor, deep trees promote reuse because of method inheritance. A high DIT has been found to increase faults. However, it’s not necessarily the classes deepest in the class hierarchy that have the most faults. It has been found that the most fault-prone classes are the ones in the middle of the tree. The root and deepest classes are consulted often, and due to familiarity, they have low fault-proneness compared to classes in the middle.
Thus an increase in DIT increases the density of bugs and decreases quality.
Number of Children
NOC = number of immediate sub-classes of a class
NOC is counted via the Inherits statement. It equals the number of immediate child classes derived from a class via the Inherits statement. High NOC indicates high reuse, since inheritance is a form of reuse. A large number of children (high NOC) may also mean improper abstraction of the parent class. If a class has too many children, it may indicate misuse of sub-classing. A class with many children may also require more testing. NOC measures the breadth of a class hierarchy, where maximum DIT measures the depth. Depth is generally better than breadth, since it promotes reuse of methods through inheritance. High NOC has been found to indicate fewer faults. This may be due to high reuse, which is desired.
Coupling between objects
CBO = number of classes to which a class is coupled
Two classes are coupled when methods declared in one class use methods or instance variables defined by the other class. The uses relationship can go either way: both uses and used-by relationships are taken into account, but only once. High CBO is undesirable. Excessive coupling between object classes is detrimental to modular design and prevents reuse. The more independent a class is, the easier it is to reuse it in another application. In order to improve modularity and promote encapsulation, inter-object class couples should be kept to a minimum. The larger the number of couples, the higher the sensitivity to changes in other parts of the design, and therefore maintenance is more difficult. A high coupling has been found to indicate fault-proneness.
Response for a Class
RFC = M + R
The response set of a class is a set of methods that can potentially be executed in response to a message received by an object of that class. RFC is simply the number of methods in the set such that a given method is counted only once in R even if it is executed by several methods M. Since RFC specifically includes methods called from outside the class, it is also a measure of the potential communication between the class and other classes. A large RFC has been found to indicate more faults. Classes with a high RFC are more complex and harder to understand. Testing and debugging is complicated. A worst case value for possible responses will assist in appropriate allocation of testing time.
Weighted Methods per Class
WMC = number of methods defined in class
WMC is simply the method count for a class. A high WMC has been found to lead to more faults. Classes with many methods are likely to be more application specific, limiting the possibility of reuse. WMC is a predictor of how much time and effort is required to develop and maintain the class. A large number of methods also means a greater potential impact on derived classes, since the derived classes inherit some of the methods of the base class. An increase in the average WMC increases the density of bugs and decreases quality. Hence for efficient performance, WMC should be kept low.
Lack of Cohesion in Methods (LCOM) metrics
Chidamber and Kemerer originally defined LCOM as follows :
“Let C1 be a class and let M1, M2, …, Mn be the member functions (methods) of C1. Let {Ii} be the set of attributes (instance variables) of C1 used by method Mi. (There will be ‘n’ such sets of attributes {I1}, {I2}, …, {In} corresponding to the ‘n’ methods of C1); LCOM = Number of disjoint sets formed by the intersection of the ‘n’ sets.”
LCOM is one of the significant metrics that can be used to evaluate an object-oriented software system. LCOM is useful for estimating the amount of cohesion in the system. For example, in an object-oriented system, the LCOM can be used to measure the cohesion of each class of the system. A high LCOM value could indicate that the design of the class is poor i.e., it might be worthwhile to split the class into two or more classes.
References
Software Engineering,A Practitioner's Approach,Roger.S.Pressman
Object Oriented Analysis and Design, Grady Booch