What is cohesion and coupling in software engineering?
By - Kaustubh Katdare • 8 months ago • 22.9k views
Hello there! As a computer science engineering student or a young professional, you might have heard the terms "cohesion" and "coupling" quite often.
These are two of the most important concepts in software engineering and they are essential for designing effective and efficient software systems.
In software engineering, cohesion refers to how closely related the functionalities within a single module are, while coupling describes the level of dependency between different modules.
Let's delve into them and understand their significance, implications, and utility.
Purpose of the Design Phase
The design phase in software engineering is where we transition from problem understanding and analysis to the solution creation.
It is a crucial step in the software development lifecycle that helps translate the requirement specifications into a blueprint for constructing the software.
During this phase, we plan and organize the system's structure and define its modules, interfaces, and data types.
The objective is to create a design that is efficient, reliable, robust, and easy to maintain. This is where the principles of cohesion and coupling come into play.
The Software Design Document and Its Relevance
The Software Design Document (SDD) is a comprehensive description of the software product to be developed.
This document details the architecture, modules, interfaces, and components that make up the system.
It serves as a roadmap for the development team and guides them through the implementation process.
In the SDD, we aim for high cohesion and low coupling. Why? Let's find out!
Understanding Cohesion and Coupling
Cohesion and coupling are the yin and yang of software engineering; they work together to achieve a balanced, efficient design.
Cohesion refers to the degree to which the elements inside a module belong together. In simpler words, it is the measure of how closely related the responsibilities of a single module are. High cohesion is generally desirable as it improves the system's maintainability and understandability, while reducing its complexity.
On the flip side, coupling pertains to the degree to which one module depends on another. It is the measure of how much one module is interconnected with another module. Lower coupling is generally sought after because it reduces the ripple effect, where a change in one module leads to changes in another.
Conceptual Design of the System
The conceptual design provides a high-level view of the system, defining its main components and the relationships between them.
This view is typically technology-independent and focuses on defining the system's structure, functions, and features.
At the conceptual level, we look at the big picture.
Here, we aim to identify modules or components that should have high cohesion (i.e., components whose internal elements are closely related) and low coupling (i.e., components that have fewer dependencies on other components).
Technical Design of the System
While the conceptual design looks at the 'what' of a system, the technical design focuses on the 'how'.
It delves into the nitty-gritty details of how the system will be implemented, including the technologies to be used, the detailed specification of each module, data types, algorithms, and more.
In the technical design, we see the principles of cohesion and coupling applied in a more tangible way.
We use these principles to ensure that our modules are self-contained (high cohesion) and that changes to one module do not affect others (low coupling).
Modularization is the process of dividing a software system into separate, independent modules. These modules can then be developed, tested, and debugged independently, leading to increased productivity and ease of understanding. Cohesion and coupling principles heavily influence the effectiveness of modularization.
Types of Coupling
There are several types of coupling, each with varying degrees of inter-module dependency:
Content coupling: This is the highest and most undesirable level of coupling. Here, one module directly alters or relies on the internal workings of another module. Such reliance on the inner details creates a very high degree of dependency and complexity. For instance, if Module A uses the internal data of Module B, any changes within Module B could directly impact Module A, potentially leading to significant errors. This makes content coupling highly unfavourable, as it tends to break the principle of information hiding—a key concept in object-oriented programming.
Common coupling: Common coupling occurs when two or more modules share the same global data. This means that each of these modules can read and write to this shared data. While less severe than content coupling, common coupling still carries a risk. A change in the shared data by one module can impact all the other modules that share that data. This kind of coupling makes the system more prone to errors and harder to maintain and understand.
Control coupling: Control coupling happens when one module controls the flow of another, passing information that directs the other module's execution. For example, Module A could be sending control flags to Module B, directing how Module B should behave. Although less tightly coupled than common or content coupling, it still has potential pitfalls. If the control instructions in the controlling module change, the dependent module must also be modified to understand the new instructions.
Stamp coupling: Stamp coupling occurs when modules share a composite data structure (such as an array or object), and use only a part of it. For instance, Module A might pass an object to Module B, but Module B only uses a few properties of that object. While this is lower coupling than the previous types, it still carries risks. If the data structure changes, it may affect all modules that use it, even if they use different parts of it.
Data coupling: This is one of the lower levels of coupling. Data coupling happens when modules share data through parameters where each parameter is a simple data item. In this scenario, modules communicate using function calls and parameters, which makes the system easier to manage as compared to the higher levels of coupling. The main disadvantage of data coupling arises when there is a change in the number or type of parameters, which requires corresponding changes in all modules that interact with it.
No coupling: This is the lowest level of coupling, and therefore, the most desirable one. In this case, modules are completely independent and do not interact with each other. This independence implies that changes in one module do not affect any other modules, thereby enhancing the maintainability and adaptability of the software system. However, achieving no coupling can be unrealistic in complex systems where some degree of interaction is necessary for the system to function as a coherent unit.
Types of Cohesion
Let’s take a closer look at each of the following types of cohesion-
Coincidental cohesion: This is the lowest form of cohesion. In coincidental cohesion, the functions within a module are related only by their physical proximity within the code, not by any logical or functional relationship. Essentially, these functions are grouped together arbitrarily without any specific reason or common goal. As a result, modules with coincidental cohesion are often hard to understand, maintain, and modify, because it's not clear how changes to one function might affect others.
Logical cohesion: In logical cohesion, elements within a module are grouped because they logically fall into the same category, even though they perform different tasks. For example, a module may contain all the error handling routines, or all the mathematical operations. While this is better than coincidental cohesion, it still has some potential issues. Since the functions within the module might be related in a general sense but don't necessarily depend on each other, changes to one function can still potentially impact others.
Temporal cohesion: Temporal cohesion refers to elements that are grouped together because they are processed at a similar point in time. This might be during the initialization of a system, the shutdown, or any other specific period. For example, a module could be created to perform all tasks necessary for startup. Though better than logical cohesion, temporal cohesion is still weak because the only relationship between functions is timing, not functionality or data.
Procedural cohesion: Procedural cohesion happens when elements within a module are grouped together because they always follow a certain sequence of execution. The functions are organized in a way that each one follows the other in a specific order to achieve a common goal. For instance, a module in a recipe application that sequentially takes inputs, processes the recipe, and displays the output, showcases procedural cohesion. While this type of cohesion is stronger than the previous types, changes to one function can still have impacts on subsequent ones in the sequence.
Communicational cohesion: Communicational cohesion is when elements are grouped together because they operate on the same data or input. The functions within the module are related by the fact that they all act on the same data. For example, a module in a payroll system that calculates taxes, retirement contributions, and net pay for an employee, would demonstrate communicational cohesion. This is a strong form of cohesion, because a change to one function is less likely to affect others, as they all operate on the same data.
Sequential cohesion: Sequential cohesion exists when elements within a module form a sequence, where the output from one element serves as the input for the next. This is a higher form of cohesion where every element contributes to the overall task, and changes to one element can have direct impacts on the next element in the sequence. An example of sequential cohesion could be a module that reads data from a file, processes the data, and then writes the results back to another file.
Functional cohesion: This is the highest and most desirable form of cohesion. In functional cohesion, all elements in the module contribute to a single, well-defined task. This means that each function within the module plays a role in achieving the same specific goal. For instance, a module that is responsible for sorting an array showcases functional cohesion. Functional cohesion makes a module very robust, as it’s easier to understand, maintain, and less prone to errors.
The goal of any software design should be to achieve the highest level of cohesion possible—functional cohesion—since this makes the software easier to maintain, understand, and less likely to contain errors.
Understanding these levels of cohesion can help guide decisions about how to group functions and responsibilities within modules.
Advantages of Low Coupling
Lower coupling provides a multitude of benefits:
Easier Maintenance: In a system with low coupling, modules are independent of each other, meaning changes to one module do not necessitate changes in others. For instance, if a bug is found in one module, or if that module requires an update or enhancement, you can make those changes without affecting the rest of the system. This independence makes the maintenance process much more manageable and less error-prone since the scope of potential problems is limited to a single module rather than spreading across the entire system.
Increased Readability: When modules have low coupling, each module can be understood independently. This is because the functionality of a given module does not depend on the internal workings of other modules. As a result, developers can understand and work with one module at a time, making the code more readable and comprehensible. This aids in both the development and maintenance phases, as developers can quickly understand and make changes to a module without needing to understand the entire system.
Better Productivity: Low coupling allows for concurrent development and testing of modules. Since each module is independent, separate teams or individuals can work on different modules at the same time without affecting each other's work. This leads to an increase in overall productivity as work can be effectively distributed and parallelized. Moreover, testing becomes more efficient since each module can be unit tested independently before integrating it into the larger system.
Higher Reusability: Independent modules are more likely to be reusable in different systems. With low coupling, a module that performs a specific task does not depend on other modules. Therefore, it can be more easily lifted from its original system and inserted into a new one with little to no modification. This can significantly reduce development time when creating new systems or adding functionality to existing ones, leading to substantial time and cost savings.Advantages of High Cohesion
Advantages of High Cohesion
High cohesion also provides numerous advantages:
Easier Understanding: In highly cohesive modules, each module is designed to perform a single, specific task. As a result, the purpose and functionality of the module can be understood quickly and easily. For instance, a module in a payroll system that is responsible only for calculating taxes is easier to comprehend than a module that calculates taxes, prints payslips, and updates employee records. When each module focuses on a single task, it simplifies the overall understanding of the system's functionality and promotes clarity in the codebase, making it more accessible to both current and future developers.
Simpler Modification: High cohesion ensures that changes are localized within a module. If a module has a single, specific purpose, any modifications or enhancements related to that purpose will likely only affect that module. For example, if tax calculation rules change, you would only need to update the "tax calculation" module in a highly cohesive system. This containment of changes reduces the risk of introducing bugs into other areas of the system, making modifications simpler and safer.
Increased Reliability: With high cohesion, there are fewer interactions between modules, meaning fewer chances for errors to occur at the interfaces. Each module can function correctly on its own, and so the potential for malfunction decreases as interactions decrease. Also, if an error does occur within a module, the impact of that error is likely to be confined to the module itself, thereby preventing a system-wide failure. This isolation improves the overall reliability of the system.
Enhanced Reusability: Just as with low coupling, high cohesion can enhance the reusability of a module. A module that performs a specific task well can potentially be used in different systems that require that task. For example, a "date validation" module from a reservation system could be reused in a payroll system to validate dates. This level of reusability can save significant development time and resources.
Disadvantages of High Coupling
High coupling has its downsides:
Lower Maintainability: In a system with high coupling, changes in one module often require changes in other modules that are dependent on it. For example, if a module's interface or functionality changes, all modules that interact with it may also need to be modified to accommodate these changes. This creates a ripple effect that can make maintenance tasks more complex and time-consuming. In addition, it increases the risk of introducing new bugs into the system when making these changes.
Complexity: High coupling tends to result in a complex, hard-to-understand system because modules are so intertwined. Each module cannot be understood in isolation, as understanding a single module often requires knowledge of other modules it interacts with. This complexity makes the system harder to understand, modify, and debug, which can slow down development and increase the chance of errors being introduced into the system.
Lower Reusability: In a highly coupled system, modules are often so interconnected that they can't be used independently in other systems. A module's functionality may be tightly tied to the functionality of other modules in the system, meaning it can't function properly without them. This decreases the module's reusability, as it can't be taken out of its original system and used in a new one without also bringing along the modules it depends on.
Disadvantages of Low Cohesion
Low cohesion comes with its own set of disadvantages:
Increased Complexity: Low cohesion means that a module is performing multiple, unrelated tasks. This can increase the complexity of the module and make it harder to understand. For example, if you have a module that calculates payroll, sends emails, and logs errors, understanding what the module does at a glance can be challenging. Developers may need to dig into the code to understand its full functionality, making the development and maintenance process more difficult and time-consuming.
Lower Maintainability: In a module with low cohesion, making changes often requires understanding and modifying a significant part of the module, since its responsibilities are scattered and intermixed. This can make maintenance tasks more complex and error-prone. For instance, if you need to modify the email sending functionality in the module mentioned above, you may need to navigate through the payroll calculation and error logging code to find the parts relevant to email. This intricacy increases the chance of inadvertently introducing errors or side effects.
Reduced Readability: Low cohesion can reduce the readability of the system, as there isn't a clear, distinct purpose for each module. When a module does many different things, it's harder for developers to quickly grasp what it does, or to find the specific functionality they're looking for. This can slow down both development and debugging processes, and can also make the system more difficult for new developers to learn.
A balanced approach to cohesion and coupling is a cornerstone of good software design.
Understanding these concepts and using them effectively, is the key to creating software that is efficient, maintainable, reliable, and highly reusable.
Note: Only logged-in members of CrazyEngineers can add replies.