CSC/ECE 517 Fall 2012/ch2a 2w20

From Expertiza_Wiki
Revision as of 23:40, 21 October 2012 by Adave (talk | contribs) (Created page with "==Problem Definition== Cohesion and coupling are concepts that are reasonably easy to understand, but nonetheless, it is worthwhile to gather readable illustrations of where they...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Problem Definition

Cohesion and coupling are concepts that are reasonably easy to understand, but nonetheless, it is worthwhile to gather readable illustrations of where they apply. Browse the hundreds of Web pages that attempt to explain these concepts, picking your favorite examples. Categorize these examples, so that the reader will see the big picture, rather than just a set of redundant illustrations. Many of these pages mention related concepts; list some of them and explain how they relate to cohesion and coupling.

Introduction

Cohesion and Coupling are two cornerstones of Object-Oriented Programming. They sound similar, but have very different meanings. Cohesion is the “act or state of sticking together” or “the logical agreement". It is the basic idea that a class has a focused set of responsibilities or behaviors from a particular perspective. In contrast to cohesion, Coupling refers to the physical connections between elements of the OO design (eg: the number of collaborations between classes or the number of messages passed between objects) within an OO system. In a simple way, it gives the measure of the interdependence of one module to another.

Cohesion

Cohesion is the "glue" that holds a module together. It can be thought of as the type of association among the component elements of a module. Cohesion is related to the Single Responsibility Principle. Generally, one wants the highest level of cohesion possible. An object with high cohesion is defined for one purpose and it performs only that purpose. An object with low cohesion tends to try to do a lot of different things. For example, if there is a Card object which is responsible for drawing itself, sending messages back to the server, and also executing game logic then it has low cohesion because that one class is attempting to do too much. A system can also have low cohesion if too many objects are attempting to do the same thing. For example, if a system has objects to implement a NetRunner game and every card has a unique class with a rendering method and that method is nearly identical in all classes, then the system has low cohesion. A good software design is always designed to achieve high cohesion. An example of a high cohesive EmailMessage class is given below [1].

class EmailMessage
{
   private string sendTo;
   private string subject;
   private string message;
   public EmailMessage(string to, string subject, string message)
   {
       this.sendTo = to;
       this.subject = subject;
       this.message = message;
   }
   public void SendMessage()
   {
       // send message using sendTo, subject and message
   }
}

The above class was originally designed to send an email message. Suppose if it is modified in the future in a way that the user needed to be logged in to send an email which is implemented by adding a Login method to the EmailMessage class.

class EmailMessage
{
   private string sendTo;
   private string subject;
   private string message;
   private string username;
   public EmailMessage(string to, string subject, string message)
   {
       this.sendTo = to;
       this.subject = subject;
       this.message = message;
   }
   public void SendMessage()
   {
       // send message using sendTo, subject and message
   }
   public void Login(string username, string password)
   {
       this.username = username;
       // code to login
   }
}

The Login method and username class variable really have nothing to do with the EmailMessage class and its main purpose which makes it a low cohesive class.

Types of Cohesion [2]

  1. Coincidental cohesion (worst)- A module has coincidental cohesion if its elements have no meaningful relationship to one another. It is explained with an example here [3].
  2. Logical cohesion- A module has Logical cohesion when parts of a module are grouped together as they are logically categorized to do the same thing, even if they are different by nature (e.g. grouping all I/O handling routines). Diagram
    Types of Cohesion
  3. Temporal cohesion- A temporally cohesive module is one whose elements are functions that are related in time. That is operations that are performed to reflect a specific behavior or state. It is explained with an example here [4].
  4. Procedural cohesion-A procedurally cohesive module is one whose elements are involved in different activities, but the activities are sequential.Diagram
  5. Communicational cohesion- A communicationally cohesive module is one whose elements perform different functions, but each function references the same input information or output.Diagram
  6. Sequential cohesion- A sequentially cohesive module is one whose functions are related such that output data from one function serves as input data to the next function. The intent is to implement a sequence of operations Diagram.
  7. Functional cohesion (best)- A functionally cohesive module is one in which all of the elements in a module performs one and only one computation and then returns a result. Object-oriented languages tend to support this level of cohesion better than earlier languages do.

Studies indicate that the first two types of cohesion are inferior, communicational and sequential cohesion are very good and functional cohesion is superior. A detailed description of each cohesion type is given here [5].

Measurement of cohesion

A high degree of cohesion is attained by:

  • Including all the relevant elements of the object together in one class instead of chaining through invisible objects (like automobileTag).For example, in the given class Person, the elements automobile tag and automobile year are not directly relevant to a person. So, we can create a new class 'Car' and include it as an element in class 'Person'.
class Person {                                      class Person {     
  String name;                                        String name;
  String automobileTag;                               Car automobile;
  String automobileYear;           ---->              }
}
                                                    class Car {
                                                     String tag;
                                                     int year;
                                                     }
  • Include all the necessary elements in the object, so that the elements presence would model the object accurately. For example, in class 'TriagePatient', it is not necessary to include the favorite TV show. One way of including the rarely used attributes (like favoriteTVshow) conditions is to create a subclass.
class TriagePatient {                              class TriagePatient {
 Boolean inShock?                                  Boolean inShock?
 String  bloodType;                                String  bloodType;
 String  favoriteTVshow;         ----->            }
}
                                                   class RecoveringPatient {
                                                   String  favoriteTVshow;
                                                   }
  • Ensure that all the necessary elements are present in the class to achieve completeness. For example, in a class like 'Stack', there should be a mechanism to push and pop elements to complete the stack. Some relevant elements may get omitted due to complexity. Knowing what to leave out is part of abstraction.
class Stack {                           class Stack {
  int size;                  ----->       int size;
  Object elems[];                        Object elems[];
  push(elem) {...}                       push(elem) {...}
}                                        pop() { }
                                         }

Advantages of high cohesion

  • Cohesion is the idea that a given thing (be it a system, and object,or a method) does a single, clearly definable thing. This has the benefit of making your code easier to follow.
  • Promotes code reuse, since small atomic blocks are easier to reuse then larger blocks.
  • High cohesion makes it easier to replace a module by another one that provides same functionality.
  • High cohesion reduces complexity of the system, and hence increase the application's reliability.
  • If the module is highly cohesive, the software is more readable and maintainable.
  • Communication between developers is easier.

Coupling

Coupling is a qualitative measure of the degree to which the classes, modules or subsystems are connected to one another. It can be defined as the amount of interaction of one object with another object, or one module with another module. For a good software design, it is always advisable to minimize coupling. Low coupling does not mean no coupling, the goal is reduction rather than elimination of coupling. A system with no coupling is, by definition, not a system. Low coupling indicates that each object or module performs independent tasks. In general, low coupling can be well explained by Law of demeter. It states that classes within a module or subsystem should have only limited knowledge of classes in other modules or subsystems. In simple terms, 'Law of Demeter' says "Each unit should only talk to its friends; don't talk to strangers". Strong coupling means that one object or module is dependent on other object or module to perform an operation or task. It simply means that the object or module is strongly coupled with the implementation details of another object or module. With low coupling, a change in one module will not require a change in the implementation of another module.

An example of a two highly coupled objects an iPod object and a Song object is given below [6]. Song class might look something like this in Ruby style.

class Song
def do(action)
if action == 1
# code to play song…
elsif action == 2
# code to pause
elsif action == 3
# code to skip
endif
end
end

In the above program, programmer who wants to interface the iPod object with the Song object is now dependent on the special meaning of 1, 2, & 3 to use the Song object correctly. The iPod object and the Song object are highly coupled. We can implement these objects in another way which makes these objects more cohesive is given below.

class Song
def new(path_to_song)
#code to get the song from the filesystem
end
def self.play
#code to play song
end
def self.pause
# code to pause song
end
end
#This would allow the iPod object to call
currentSong = Song.new(”/home/user/Music/1812_Overture.mp3″)
currentSong.play
currentSong.pause

With the above code, other objects that wish to interact with it can do so in a uniform way regardless of how the Song object is implemented on the backend. They just know that Song object does what it is supposed to do.

Types of Coupling [7]

  1. No Direct Coupling (low) - These are independent modules and so are not really components of a single system.
  2. Data Coupling - Two modules are data coupled if they communicate by passing parameters or data arguments. Increase in data coupling results in increased complexity of interfaces, because the bandwidth of communication between classes increase Diagram.
    Types of Coupling
    .
  3. Stamp Coupling - Two modules are stamp coupled if they communicate via a passed data structure that contains more information than necessary for them to perform their functions. For example, class B is declared as atype for an argument of an operation of class A. Therefore, modifying the system could be complicated as now class B is a part of the definition of class A. Diagram
  4. Control Coupling - Two modules are control coupled if they communicate using at least one "control flag". For example, operation X() invokes operation Y() and passes control flag to Y. In this case, changing code of operation Y may require change in flag value passed by X. Diagram.
  5. Common Coupling - Two modules are common coupled if they both share the same global data area. It is useful to establish values that are common to entire application, however, it can lead to uncontrolled error propagation that can be difficult to trace. Diagram
  6. Content coupling (high)- Content coupling is when one module modifies or relies on the internal workings of another module (e.g. accessing local data of another module). Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module. This violates information hiding - a basic object oriented design concept.

In object-oriented programming, Subclass coupling describes a special type of coupling between a parent class and its child. It describes the relationship between a class and its parent. The class is connected to its parent, but the parent isn't connected to the child.

To achieve low coupling

1. Strive for low message coupling. It can be achieved in the following ways -

  • Number of messages passed between objects can be reduced.
  • Messages can be simplified using few parameters.
  • Avoid requiring multi-message sequences.
car.pushPedal(CLUTCH);
car.shiftToGear(PARK);        --->         car.start(carKey);
car.insertKey(carKey);
car.pushPedal(GAS);
car.turnKeyInIgnition();

2. Strive for low association coupling. It can be achieved by -

  • Reduce extent to which objects depend on the internal structure of each other.
  • Reduce the use of superclasses instance variables by subclasses (strong form of the Law of Demeter).
class Person {
  String first, last;                         class Doctor : Person {
  getName { first + last }                    getName {
  }              ^            ----->           "Dr" + Person::getName();
                 |                                }
                 |                            }
class Doctor : Person {
  getName {
    "Dr" + first + last;
  }
}

3. Strive for moderate inheritance coupling. It can be achieved in the following ways -

  • Abstract so that subclasses depend on the methods (but not the structure!) of their superclasses.
  • Use or refine as many of superclass' operations as possible in the child classes.
class Person {
  hello { 
     print "Hi!"                               class Doctor : Person {
  }                                             greetings {
}              ^            ---->                hello();
               |                                 }
               |                                }
class Doctor : Person {                          
  greetings {                                  
     print "Hi!";
  }
}

Disadvantages of high coupling

  • Decreases the flexibility of the application software. Developers / maintenance programmers need to understand potentially the whole system to be able to safely modify a single component.
  • Object interaction complexity associated with coupling can lead to increased error generation during development.
  • Decreases the scalability of the application software. Changing requirements in one part of software will potentially require wide ranging changes in the entire application.
  • Decreases the maintainability of the application software. More thought need to go into choices at the beginning of the lifetime of a software system in order to attempt to predict the long term requirements of the system because changes are more expensive.
  • Testability is likely to degrade with a more highly coupled system of objects.

Cohesion and Coupling Examples

As the popular proverb says " A picture is worth a thousand words ", we will try to represent cohesion and coupling in terms of visual programming metaphors. Observe Figures 1 and 2.

Figure1: Loosely Coupled & Highly Cohesive

What can one say about the two diagrams? Which one is easy to understand, remember and modify? Obviously the answer would be Figure 1. This is exactly why it is recommended to have high cohesion and loose coupling in design or implementation of a software application. The two figures gives a clear visualization of the underlying concepts of coupling and cohesion.

Figure2: Highly Coupled & Loosely Cohesive

.

In General

Let us take a look at some simple real life examples to understand the relation between these two terms.

Example 1. Project team - If we assume that a certain project has 20 people working on it, the possible communication channels would be around 400. The team would be very inefficient with high communication overhead. It can be viewed as an example of tight coupling. Therefore, in any team, certain roles and responsibilities are formally assigned to team member, to enhance productivity of the team with smooth coordination. Well defined roles of a team member can be viewed as high cohesion, and establishing communication channels (like defining hierarchy or reporting structure) can be viewed as low coupling. This is a very practical example of low coupling and high cohesion comes.

Example 2. Car - Let us consider another real life example of a car. It has the engine, tire, steering wheel, gear box, brakes etc. Each component of the car performs a given set of functions. Therefore, if the car breaks down, the problem can be analyzed and only certain part needs to be repaired. This can be considered as high cohesion, as each component focuses on the assigned task. At the same time, in order drive the car, all these parts need to interact with each other. This can be viewed as coupling.

Example 3. Child and Parent relationship - Cohesion and coupling can be well expressed using the analogy of a parent child relationship. The child inherits characteristics of its parents. This can be viewed as coupling. At the same time, there is no coupling in the siblings.

In Object Oriented terms

Polymorphism and encapsulation are two major design principles of object oriented programming. In object oriented terms, we can say that, the main vehicle of coupling is polymorphism [8] and the main vehicle of cohesion is encapsulation. More explanation is provided in cohesion and coupling.

In order to achieve modularity, we need to have low coupling and high cohesion.“Modularity is the property of a system that has been decomposed into a set of cohesive and loosely coupled modules.” Booch 1994

Principle: Modularity = Low Coupling +  High Cohesion

It can be simply understood as, breaking something complex into set of manageable pieces. Consider the example of order processing system. If we decide to write entire software in only one program, it will become lengthy, unmanageable, complicated and hard to debug. Instead, we can divide it into modules or subsystems like order entry, order processing, billing and complaints.

Conclusion

Code that is easy to maintain and reusable can be attained by sticking to loose coupling and high cohesion in objects. The concept of coupling is usually related to the concept of cohesion so low coupling facilitates high cohesion, and vice versa. It is difficult to obtain perfect high cohesion and low coupling throughout the program. Once all the unitary functions that depend only on their inputs are written, a bit of less cohesive and more coupled code is required to glue them together into a working program. But, there are also times where tight coupling is desirable. Tighter the coupling, it improves performance as it reduces the cost of interfaces. So, tight coupling is important when one process is very stable and wants maximum performance and loose coupling comes into play when flexibility is required rather than stability. There can also be situations where even though high cohesion is attained, tight coupling is also present. This means though it may be logically laid out, it's tough to change because of tight interdependencies which is pretty bad. The other kind of bad software is eventhough the module is loosely coupled but it may lack cohesion. That means that there are few explicit interdependencies between modules, but none of the modules are necessarily authoritative about any particular aspect of the system. This in turn results in a scattergun approach to ongoing system evolution as even minor changes ripple through multiple components or systems. As to conclude, the objective of a good software should be to achieve high cohesion and low coupling.

See Also

External links


Back to the Assignment Page





Cohesion and coupling. Cohesion and coupling are concepts that are reasonably easy to understand, but nonetheless, it is worthwhile to gather readable illustrations of where they apply. Browse the hundreds of Web pages that attempt to explain these concepts, picking your favorite examples. Categorize these examples, so that the reader will see the big picture, rather than just a set of redundant illustrations. Many of these pages mention related concepts; list some of them and explain how they relate to cohesion and coupling.


Introduction

Cohesion and Coupling are concepts often discussed when referring to object-oriented programming. In fact, one of the primary goals of object-oriented programming is “to build highly cohesive classes and to maintain loose coupling between those classes” [9]. This begs the question as to what is meant by cohesive classes and how do we know when classes are highly cohesive? Further, what does loose coupling mean and how do we know it has been maintained? This article combines information and examples gathered from the Web in an effort to shed some light on these very relevant questions.

In the first section of this article, we provide the basic definition of cohesion along with examples that describe the different types of cohesion. Next, we explain the concept of coupling and use more examples to illustrate the different types of coupling. In the third section of this article, we highlight some of the concepts related to cohesion and coupling, including ways to measure both and an illustration of Demeter’s Law. Lastly, we provide a conclusion to the topics discussed herein.

Cohesion

Cohesion refers to the degree that a module performs one and only one function. In this context, a module refers to any logical collection of “program entities” [10]. This logical collection of program entities could be a function, a class, or even a package. As stated earlier, the goal of object-oriented programming is to achieve highly cohesive classes. High cohesion is desired because it increases the likelihood that a module will be comprehendible, reliable, robust, and reusable [11]. Alternatively, a module that exemplifies low cohesion is difficult to comprehend, costly to maintain, and less likely to be reused [12]. The following examples illustrate the different types of cohesion and are arranged from lowest (least desirable) to highest (most desirable).

Coincidental Cohesion

Coincidental cohesion describes a module whose operations are unrelated to one another and the module itself can be used to achieve several different types of tasks. The following example illustrates coincidental cohesion [13].

 public static class BackendService {
   public static float computeNetPay(int orderId) {
     // implementation
   }
   
   public static float calculateInventoryReorderAmount(int inventoryId) {
     // implementation
   }
 
   public static boolean generateInvoice(int invoiceId) {
     // implementation
   }
   
   // ...
 }

In this example, the BackendService provides a one stop shop for many different types of tasks. This type of cohesion is the least desirable and should be avoided.

Logical Cohesion

Logical cohesion describes a module that groups operations because categorically they are related but the operations themselves are quite different. Typically, these modules accept a control flag which indicates which operation to execute. The following example illustrates logical cohesion.

 public class DataStore {
   public void SaveData(int destination, byte[] data) {
     switch (destination) {
       default:
       case 0:
         SaveToDb(data)
         break;
       
       case 1:
         SaveToFile(data)
         break;
       
       case 2:
         SaveToWebService(data)
         break;
     }
   }
   
   protected void SaveToDb(byte[] data) {
     // implementation
   }
   
   protected void SaveToFile(byte[] data) {
     // implementation
   }
   
   protected void SaveToWebService(byte[] data) {
     // implementation
   }
 }

In this example, although all of the operations are logically related by the fact that they all save data, the truth is they are in fact quite different. Saving to a database likely requires a completely different implementation than saving to a file or web service.

Temporal Cohesion

Temporal cohesion describes a module that has several operations grouped by the fact that the operations are executed within temporal proximity. The following example illustrates temporal cohesion.

 public class Startup {
   public void Initialize() {
      InitializeLogging();
      
      InitializeUI();
      
      InitializeDb();
   }
   
   public void InitializeLogging() {
     // implementation
   }
   
   public void InitializeUI() {
     // implementation
   }
   
   public void InitializeDb() {
     // implementation
   }
 }

This example highlights a common implementation pattern where tasks are grouped simply by the fact that they need to be executed at around the same time. Aside from needing to be executed at the same time, these operations implement tasks that are indeed quite different.

Procedural Cohesion

Procedural cohesion describes a module whose operations are typically grouped because they are executed within a sequence. Unlike sequential cohesion (discussed below), the operations within a procedurally cohesive module can be somewhat unrelated and output from one operation is not necessarily used as input to a subsequently executed operation. The following example illustrates procedural cohesion.

 public class GradeBookSystem {
   public void Login(int teacherId) {
     // implementation
   }
   
   public ArrayList GetStudents(int teacherId) {
     // implementation
   }
   
   public void UpdateGrades(ArrayList grades) {
     // implementation
   }
   
   public void UpdateAttendance(ArrayList dates) {
     // implementation
   }
   
   public void Logout(int teacherId) {
     // implementation
   }
 }

In this example, the GradeBookSystem exposes different tasks that allow the teacher to login, track student grades/attendance and logout. These operations are grouped in a procedurally cohesive manner to facilitate the higher level task of upgrading the teacher's grade book.

Communicational Cohesion

Communicational cohesion describes modules that perform multiple operations on the same input or output data. The following example illustrates communication cohesion.

 public class CustomerInformation {
   public CustomerInformation(int accountNum) {
     // implementation
   }
   
   public String getName() {
     // implementation
   }
   
   public float getBalance() {
     // implementation
   }
   
   // ...
 }

In this example, the getName and getBalance operations facilitate tasks against the common input accountNum.

Information cohesion

Communicational cohesion describes modules that perform several tasks against a shared data structure. The following example illustrates information cohesion.

 class RectangleTransformer {
 
   Rectangle rectangle;
   
   public RectangleTransformer(Rectangle rectangle) { 
     this.recentangle = rectangle; 
   }
 
   public double getArea() {
     // implementation 
   }
 
   public double getPermiter() {
     // implementation 
   }
 
   public void flip() {
     // implementation 
   }
   
   public void stretch(double width, double height) {
     // implementation 
   }
 }

In this example, all of the RectangleTransformer operations are performing actions against the shared rectangle member variable.

Functional Cohesion

Functional cohesion describes a module that is designed to perform one and only one task. A functionally cohesive module may contain multiple methods, but all of these methods are designed to help the user achieve a single task. The following example illustrates functional cohesion.

 public class Stack {
   public Stack() {
     // implementation
   }
   
   public void push(Object obj) {
     // implementation
   }
   
   public Object pop() {
     // implementation
   }
 
   public Object peek() {
     // implementation
   }
   
   public int isEmpty() {
     // implementation
   }
 }

In this example, all of the operations in the Stack class facilitate the task of supporting a Stack data structure.

Advantages and Disadvantages

Cohesion is the idea that the module does a single task - be it calculating data, checking file etc. The "single task mindedness" drastically reduces code breaking when other modules are changed. If the module uses data from multiple other modules - if even one module changes or breaks, this module might need to be changed thus more time wasted. With single task modules, individual modules can be changed with very little problem.

Coupling

Coupling refers to the degree of connectedness between two modules. Modules that have many connections between them are said to be highly or tightly coupled. Alternatively, modules that have very little connection between them are said to be lowly or loosely coupled. Introducing more coupling between modules, increases the interdependencies between modules. As a result, attempting to reuse one module requires the “import” of all of its associated or coupled modules [14]. In addition, high coupling may introduce issues with code reuse, maintenance, testing, and comprehension of the relationships between modules [15]. For these reasons, it is important to maintain loose coupling between classes. The following examples illustrate the different types of coupling and are arranged from tightest (least desirable) to loosest (most desirable).

Content coupling

Content coupling occurs when one or more modules access the internals of another module. The following example illustrates content coupling.

 public class Rectangle {
 
   public int Top = 0;
   public int Left = 0;
   public int Width = 0;
   public int Height = 0;
   
   public Rectangle(int top, int left, int width, int height) {
     this.Top = top;
     this.Left = left;
     this.Width = width;
     this.Height = Height;
   }
    
   public int getArea() {
     return this.Width * this.Height;
   }
 }
 public class FloorPlan {
   Rectangle rectangle = null;
 
   public FloorPlan(int width, int height) {
     rectangle = new Rectangle(0, 0, 50, 100);
   }
 
   public void modifyDimensions(int width, int height) {
     rectangle.Width = width;
     rectangle.Height = height;
   }
   
   public int getArea() {
     return rectangle.getArea();
   }
 }

In this example, FloorPlan is able to directly modify the Width and Height fields of the Rectangle object. This coupling creates a dependency from FloorPlan on the internals of the Rectangle object that inhibits maintenance of the Rectangle class. If someone wanted to go back and change the Width and Height fields of Rectangle class to use a different data type they would also have to update the FloorPlan class.

Common coupling

Common coupling occurs when two or more modules modify the same same global variable. The following example illustrates common coupling.

 #include <stdio.h>
 #include <string.h>
 
 #define NUM_FIELDS 3
 
 class EmployeeRecordParser {
   public:
     EmployeeRecordParser(char* strRow, int nFields) : m_nCount(nFields), m_aryFields(0) {
 
       m_aryFields = new char*[m_nCount];
 
       char* strField = strtok(strRow, ",");
   
       for (int ct = 0; ct < m_nCount && strField; ++ct) {
 
          m_aryFields[ct] = new char[strlen(strField) + 1];

          memcpy(m_aryFields[ct], strField, strlen(strField));
 
          m_aryFields[ct][strlen(strField)] = 0;
 
          strField = strtok(NULL, ",");
       }
      }
  	
     ~EmployeeRecordParser() {
        if (m_aryFields)			
          delete [] m_aryFields;
      }
 
      int GetCount() { return m_nCount; }
      char* operator[](int nIndex) { return GetField(nIndex); }
      char* GetField(int nIndex) { return nIndex < m_nCount ? m_aryFields[nIndex] : ""; }
  
    private:
      char**	m_aryFields;
      int	m_nCount;
 };
 void ParseRecords(char* strFile) {
   int nRecords = 0;
   char* strRow = strtok(strFile, "\n");
 
   while (strRow) {		
 
     EmployeeRecordParser record(strRow, NUM_FIELDS);
 
     printf("\nEmployee Record %d\n------------------------\n", ++nRecords);
 
     for (int i = 0; i < record.GetCount(); ++i)  {
       printf("Field %d: %s\n", i, record[i]);
     }
 
     strRow = strtok(NULL, "\n");
   }
 }
 
 int main() {
   char str[] = "Tom,Frank,919-777-2333\nMikel,Dundlin,919-234-5512\nRobert,Skoglund,919-232-2904";
 
   ParseRecords(str);
  
   return 0;
 }

In the C++ example above, both the ParseRecords method and the EmployeeRecordParser class make use of the globally accessible strtok function. Internally, strtok uses a static variable to track the position of the current string being tokenized, which is also used to determine when the whole string has been parsed. In this particular example, the coupling on this common function has a side effect that causes a bug that prevents all the records from being correctly parsed.

Control coupling

Control coupling occurs when one module controls the execution flow of another module. The following example [16] illustrates control coupling.

 enum InfoType { id, name, balance }
 
 public class CustomerInfo {
   public Object getCustomerInfo(InfoType type) {
     Object returnVal = null;
     switch (infoType) {
       case InfoType.id:                    
         returnVal = getCustomerId();
         break;
 
       case InfoType.name:
         returnVal = getCustomerName();
         break;
 
       case InfoType.balance:
         returnVal = getCustomerBalance();
         break;
     }
     
     return returnVal;      
   }
 
   // ...
 }
 public class Client {
   private customerInfo = new CustomerInfo();
 
   public void execute() {
     int id = (int)customerInfo.getCustomerInfo(InfoType.id);
     // ...
   }
 }

In this example, the Client class controls the flow of execution within the CustomerInfo module. This form of coupling requires modules calling CustomerInfo to know about the flow of execution within its class.

Stamp coupling

Stamp coupling occurs when two or more modules access or modify the same data of a shared object. The following example illustrates stamp coupling.

 public class Customer {
   private int id = 0;
   private String name = "";
   private float balance = 0.0f;
 
   public int getId() { return id; }
 
   public void setId(int _id) { id = _id; }
 
   public String getName() { return name; }
 
   public void setName(String _name) { name = _name; }
 
   public float getBalance() { return balance; }
 
   public void setBalance(float _balance) { balance = _balance; }
 }
 public class CustomerInfo() {
   public void save(Customer customer) {
     int id = customer.getId();
     String name = gustomer.getName();
     // ...
   }
 }
 public class Client {
   private customerInfo = new CustomerInfo();
 
   public void execute() {
     Customer customer = new Customer();
 
     customer.setId(5);
     customer.setName("Example");
     customer.setBalance(100f);
     
     customerInfo.save(customer);
   }
 }

In this example, the Client and CustomerInfo classes share the common Customer class. This is a desired form of coupling.

Data coupling

Data coupling occurs when one module passes primitive type or simple data structure to another module as an argument. The following example illustrates data coupling.

 public class CustomerInfo
 {
   public float getCustomerBalance(int customerId)
   {
     // implementation details
   }
 }
   
 public class Client
 {
   private customerInfo = new CustomerInfo();
   
   public void execute(int customerId)
   {
       float balance = customerInfo.getCustomerBalance(customerId);
 
       // ...    
   }
 }

In this example, Client and CustomerInfo interact using only primitive types of data. This is a desired form of coupling.

Advantages and disadvantages

Coupling allows interaction between different modules so more complicated tasks can be done. However, a strong coupling will decrease the flexibility of the modules and it will be harder to main and understand. If coupling is too tight, changing one module might have a "snowball effect" and will require changes of other modules that are dependent on it. Coupling must be used with caution and modules must use exactly what it needs and nothing more.

Related Concepts

When researching cohesion and coupling there are some related concepts that repeatedly appear. Below we discuss some related concepts to cohesion and coupling.

Measuring Cohesion

The goal of well-designed systems is to have highly cohesive modules. Below are three metrics that can be used to determine the level of cohesion within a system.

Lack of Cohesion 1 (LCOM1)

 LCOM1 = (P > Q) ? (P – Q) : 0
 
 P = Total number of method pairs that do not use a common field of the class.
 Q = Total number of method pairs that access at least one common field of the class.

Lower LCOM1 values indicate higher cohesion and better overall design.

Lack of Cohesion 2 (LCOM2)

 LCOM2 = 1 – sum(mA)/(m*a)
 
 m = Total number of methods in the class.
 a = Total number of attributes in the class.
 mA = Total number of methods that access attribute a.
 sum(mA) = Sum of all mA for all attributes of the class.

Lower LCOM2 values indicate higher cohesion and better overall design. If the total number of methods or attributes is zero than the value of LCOM2 is undefined.

Lack of Cohesion 3 (LCOM3)

 LCOM3 = (m – sum(mA)/a) / (m – 1)
 
 m = Total number of methods in the class.
 a = Total number of attributes in the class.
 mA = Total number of methods that access attribute a.
 sum(mA) = Sum of all mA for all attributes of the class.

LCOM3 values greater than one indicates low cohesion and should be addressed. If the total number of methods is less than two or the number of attributes is zero than the value of LCOM3 is undefined.

Measuring Coupling

While it is impossible to avoid some level of coupling within systems, the goal is to reduce coupling as much as possible. Below are three metrics that can be used to determine the level of coupling within a system.

Coupling Between Objects (CBO)

 CBO = sum(t)
 
 t = Total number of types that are referenced by a particular class, not including any possible super-classes, 
 primitive types or common framework classes.

Lower CBO values indicate lower coupling.

Data Abstraction Coupling (DAC)

 DAC = sum(a)
 
 a = Total number of types that are used for attribute declarations, not including primitive types, common framework classes, 
 or types that are inherited from any possible super-classes.

Lower DC values indicate lower coupling.

Method Invocation Coupling (MIC)

 MIC = nMIC / (N – 1)
 
 N = Total number of classes defined within the project.
 nMIC = Total number of classes that receive a message from the target class.

Lower MIC values indicate lower coupling.

Demeter's Law

Demeter's Law is a design principle that when applied to object-oriented programming means that object A can reference object B but object A cannot use object B to reference object C. Complying with this principle prevents object A from knowing that object B uses object C thereby reducing coupling. If object A needs to access a function of object C then it is up to object B to expose an operation encapsulating the reference to object C. The following example [17] illustrates how this could be done.

 public float calculateTotal(Order order) {
    return order.getProducts().getTotalCost();
 }

In the example object the object which implements calculateTotal() is calling getTotalCost on a Products object which is exposed through order. An alternative to this approach would be for the order object to expose this functionality as suggested by the following example.

 public float calculateTotal(Order order) {
   return order.getTotalCost()
 }
 
 public class Order {
   // ...
 
   public float getTotalCost() {
     return products.getTotalCost();
   }
   
   // ...
 }

Conclusion

Within this article, we have discussed the basic premise of cohesion and provided examples that illustrate the varying degrees of cohesion. By creating modules that are highly cohesive, programmers can prevent costly maintenance, while increasing the readability, reliability, and reusability of their code. Often related to the concept of cohesion is coupling, which we also discussed. Coupling also has varying degrees and we illustrated each of these using several examples. By reducing coupling between modules, programmers can encourage code reuse as well as simplify maintenance and testing of their software. It is important to consider the relationship between these two similar concepts. As mentioned in the introduction, the goal of object-oriented programming is to have highly cohesive, loosely coupled classes. Applying loose coupling will often promote high cohesion and creating highly cohesive modules will often result in loose coupling [18]. Experience tells us, however, that it is not always possible to achieve the highest degree of cohesion and the lowest degree of coupling. Consequently, good engineers should use metrics to measure the degree of cohesion and coupling in their programs and be familiar with the different types of cohesion and coupling so that they may be successful in achieving one of the primary goals within object-oriented programming.

See also

References

  1. http://javaboutique.internet.com/tutorials/coupcoh/
  2. http://www.cs.kent.edu/~jmaletic/cs63901/downloads/CouplingCohesion.pdf
  3. http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29
  4. http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29
  5. http://blogs.ittoolbox.com/eai/implementation/archives/design-principles-cohesion-16069
  6. http://www.site.uottawa.ca:4321/oose/coupling.html
  7. http://en.wikipedia.org/wiki/Coupling_%28computer_science%29
  8. http://it.toolbox.com/blogs/enterprise-solutions/design-principles-coupling-data-and-otherwise-16061
  9. http://javaboutique.internet.com/tutorials/coupcoh/index-2.html
  10. http://javaboutique.internet.com/tutorials/coupcoh/index-2.html
  11. http://www.waysys.com/ws_content_bl_pgssd_ch06.html
  12. http://bmrc.berkeley.edu/courseware/cs169/spring01/lectures/objects/sld001.htm
  13. http://www.cs.sjsu.edu/faculty/pearce/modules/lectures/ood/metrics/Cohesion.htm
  14. http://class.ee.iastate.edu/berleant/home/Courses/SoftwareEngineering/CprE486fall2004/designModularity.htm
  15. http://www.eli.sdsu.edu/courses/spring99/cs535/notes/cohesion/cohesion.html#Heading8
  16. http://www.site.uottawa.ca:4321/oose/index.html#sequentialcohesion