Software Engineering Methodologies

Coupling & Cohesion

Michael L. Collard, Ph.D.

Department of Computer Science, The University of Akron

Layered Systems

  • System
  • Subsystem
  • Package/Module
  • Class/File
  • Attributes & Operations
  • Local variables

Why Separate into Multiple Elements?

  • Enhanced clarity and comprehensibility
  • Enhanced reuse both internally and externally
  • Reduced maintenance overhead
  • Streamlined testing procedures
  • Facilitates component replacement

Cohesion

degree of connectivity among the elements of a single module and in Object-oriented design, a single class/object

  • Internal measure on an individual class/module/method
  • How to measure?

Desired Interaction: Cohesion

Maximize internal interaction (intramural) among subelements

  • Easier to comprehend
  • Easier to test

Types of Cohesion

  • Informational (best)
  • Functional (best)
  • Sequential (better)
  • Communicational (better)
  • Procedural
  • Temporal
  • Logical
  • Coincidental (worst)

Coincidental Cohesion

  • Performs multiple, completely unrelated actions
  • Maybe based on factors outside of the design: personnel, company organization, history, avoidance of small modules
  • No reusability, challenging to maintain or enhance

Logical Cohesion

  • Performs a series of related actions, one of which is selected by the calling module
  • Parts have a logical association, but the association is not the primary logical association
  • Primary logical association should be based on the highest level of abstraction the element is involved in

Logical Cohesion (cont)

  • Often includes both high and low-level actions (in terms of abstraction) in the same class
  • Often includes unused parameters for specific uses
  • Difficult to understand interface. Many unrelated actions
  • In OO, we put methods near the abstract concept that they work on

Temporal Cohesion

  • Perform a series of actions that are related by time occurrence
  • Often happens in initialization or shutdown
  • Degrades to Logical Cohesion if the time of action changes
  • Additional subsystems may require additions to multiple modules, e.g., shutdown, startup, etc.
  • In OO, we build time occurrence actions into the class. Externally, we control lifetime.

Procedural Cohesion

  • Action based on the ordering of steps
  • Related by usage in ordering
  • Changes to the ordering of steps or purpose of steps require changing the element abstraction
  • Situations where this particular sequence applies are often limited
  • Each element, including methods/operations, should have one cohesive action

Communicational Cohesion

  • Works on all the same data
  • Action based on the ordering of steps
  • Actions are related but still not wholly separated
  • Element cannot be reused
  • Maybe the highest cohesion possible/desirable

Sequential Cohesion

  • Place actions together because they are performed in sequence
  • Not necessarily bad unless there is not a crisp abstraction represented in the name of the method

Functional Cohesion

  • Element (methods) that performs a single action or achieves a single goal
  • Maintenance involves the entire element
  • High reuse because the element is entirely independent (in its actions) of other elements
  • Easily replaced for performance, testing, platform changes, etc.
  • Superior

Informational Cohesion

  • Performs multiple actions
  • Each action has a unique entry point and independent code
  • All actions are performed on a shared data structure
  • Truly Object-Oriented
  • Superior

Coupling

  • Dependency between elements
  • Degree or reliance between elements
  • Increasing cohesion may lead to more coupling (Why?)
  • Entropy of systems and designs increases coupling
  • Coupling needs to be limited and controlled

Desired Interaction: coupling

  • Minimize external interaction (extramural) with other elements

  • Easier to comprehend
  • Easier to test
  • Can be used independently
  • Easier to replace

States of Coupling

  • tightly coupled
    • High degree of coupling between elements
    • Very difficult to develop/test/maintain/use separately
  • loosely coupled
    • Low degree of coupling between elements
    • Much easier to develop/test/maintain/use separately
  • decoupled
    • Elements with zero to minimal coupling between elements
    • Can be developed/tested/maintained/used separately
    • Common refactoring activity

Types of Coupling

  • Message Coupling (very best)
  • Data Coupling (best)
  • Stamp Coupling
  • Control Coupling
  • Common Coupling
  • Content Coupling (worst)

Content Coupling

  • A module directly references the content of another module:
    • One module, p, modifies a statement of another module, q
    • One module, p, references or alters the local data of another module, q
    • One module, p, branches into another module, q
  • Content-coupled modules are inextricably interlinked
    • Change to module q requires a change to module p, including recompilation
    • Reusing module p requires using module q

Content Coupling Example: Direct Access to Internal Data

Content Coupling Example: Branching Based on Type

Content Coupling Examples: Assuming Internal Layout or Ordering

  • Accessing an array or list from another module and making assumptions about its order or layout.

Content Coupling Example: Subclassing for Access

Content Coupling Example: Using "Friend" Improperly

Content Coupling Example: Dependence on Physical Storage Format

  • One module making assumptions or directly accessing the physical storage format used by another module, such as a specific file format or database schema

Common Coupling

  • Using global variables, i.e., global coupling
  • All modules have read/write access to a global data block
  • Modules exchange data using the global data block (instead of arguments)
  • Single module with write access where all other modules have read access is not common coupling

Common Coupling Example: Shared Global Variables

Common Coupling Example: Singleton Pattern Misuse

Common Coupling Example: Database Access

Multiple modules accessing and modifying the same database tables without any form of isolation

  • A change in the schema of a table could affect all modules that directly access that table

Common Coupling (cont)

  • To determine why a variable has a particular state, you have to examine all the modules
  • Side effects, so all the code in a function needs to be examined
  • Changes to the data layout in one module require changes in all other modules
  • Must declare the identical list of global variables for module reuse
  • Module is exposed to more data than is needed

Control Coupling

  • One module passes an element of control to another module
  • One module explicitly controls the logic of another
    • Control switch passed as an argument
    • Module p passes an argument to module q that directly tells it what control structure path to take

Control Coupling (cont)

  • In control coupling, the fact that you have an if-statement has leaked out to the caller
  • Independent reuse is not possible
  • Modules should pass data and leave control path decisions private to a module

Control Coupling Solution

Stamp Coupling

  • One module passes more data than needed to another module
  • Often involves records (structs) with lots of fields
  • Entire record is passed but uses only a few fields
  • Efficiency considerations?

Data Coupling

  • Only required data is passed from one module to another
  • All arguments are homogenous data items
    • Simple data type
    • Complex data type, but uses all parts
  • Allows for comprehension, reuse, maintenance, security, …

Message Coupling

  • Parameters passed via a non-private data format
  • Most flexible, since you can generate data using any language, tool, etc.
  • Requests can be stored, cached
  • All arguments are homogenous data items
    • Simple data type
    • Complex data type, but use all parts
  • Allows for comprehension, reuse, maintenance, security, …
  • Potential performance overhead (?)

Comparison

  • Tightly Coupled
    • Change in one module leads to changes in other modules
    • Difficult to test/reuse individual parts
    • Difficult to assemble separately
    • Difficult to add additional feature/use of the system
  • Loosely Coupled
    • Allows independent changes to modules
    • Easily test/reuse individual parts
    • Easier to separately assemble
  • If modules cannot be loosely coupled, perhaps they should be combined (or packaged together) or used in precise ways

Other Types of Coupling

  • Subclass Coupling
  • External Coupling
  • Global Coupling
  • Temporal Coupling
  • Logical Coupling
  • Implicit Coupling
  • Subsystem Coupling
  • Semantic Coupling
  • Ad-hoc Coupling
  • Dynamic Coupling
  • Functional Coupling

Subclass Coupling

Between derived and base class

  • UML Generalization (object-oriented inheritance) leads to very high coupling between the derived class and the base
  • Strongest UML relationship
  • However, well understood

External Coupling

When a component relies on an external system or service. For instance, coupling to a specific database engine or a third-party library.

  • Database Engine A module that directly uses SQL queries specific to MySQL would be externally coupled to MySQL
  • Third-Party Libraries A class that relies on a specific version of a third-party JSON parsing library is externally coupled to that library
  • Operating System APIs Software that directly calls Windows APIs would be externally coupled to the Windows operating system

Global Coupling

When several modules share global data. Global variables are the most common form of this.

  • Shared Configuration Multiple modules reading from a global configuration object
  • Singletons Several classes interact with a singleton logging service
  • Global Variables Multiple functions relying on a globally declared variable for program state

Temporal Coupling

When the timing and order of operations are critical. For example, one function must be called before another to maintain state.

  • Initialization Functions A function init() must be called before using other functions from a module
  • Authentication A user must be authenticated before making API calls
  • State Machine The state of an object must transition from IDLE to READY before it can process tasks

Logical Coupling

Sometimes, different modules are coupled so that they must be modified together whenever a change occurs. Even if the modules do not directly interact, a change in one often requires changes in the others.

  • Feature Flags Multiple modules need to be adjusted whenever a particular feature flag is toggled
  • Constants File Various modules read values from a single constants file; changes to this file affect all of them
  • Multi-Step Processes When implementing a checkout process, if you change the payment module, you might also need to update the invoice and shipping modules

Implicit Coupling

Sometimes, coupling is not obvious and is based on assumptions. For instance, two classes might be implicitly coupled if they rely on the same configuration setting, even if they do not directly interact.

  • Assumed File Structure Multiple classes expect a specific directory structure but don't explicitly enforce or check it
  • Assumed Protocol A module sends data over a network assuming that the receiver knows how to decode it
  • Unstated Dependencies A function expects the caller to set a specific environment variable but does not check or declare it explicitly

Subsystem Coupling

In large systems, different subsystems can have varying degrees of coupling between them

  • Microservices Two separate microservices are tightly coupled because they share a database schema
  • Plugin Architecture A core system and its plug-ins have mutual dependencies
  • Frontend-Backend The frontend is tightly coupled to backend API specifics, making it hard to change one without the other

Semantic Coupling

This occurs when modules depend on some conceptual or domain-specific logic that isn't explicitly coded. This can be hard to spot just by looking at the code, as it may require domain knowledge to identify.

  • Business Logic One module calculates taxes; another applies discounts. Both need to know the rules around taxable items
  • Ordering Assumptions One service processes orders and another ships them; both must understand the order statuses
  • Shared Domain Concepts Both the inventory and the sales modules have to understand what a "product" is but are not directly linked in code

Ad-hoc Coupling

A random or unplanned coupling that occurs without any logical relationship between the coupled modules. Such coupling is generally considered undesirable as it can be error-prone and make the system harder to understand.

  • Random Utility Functions A utility function that converts temperatures is oddly used in a financial calculation module
  • Unplanned Shortcuts A method in a logging class also sends email alerts, making it coupled with the email system
  • "Quick Fixes" To quickly patch a bug, a developer makes an unauthorized call to a private method from another class

Dynamic Coupling

In dynamic languages or runtime-generated code, coupling can happen dynamically during the execution of the program. This makes it more difficult to analyze and manage.

  • Reflection in Java Using Java reflection to dynamically load classes
  • Dynamic Script Loading JavaScript code that dynamically imports other JavaScript files
  • Runtime Code Generation A system that generates SQL queries dynamically based on user input

Functional Coupling

This refers to the situation where one function calls another function and relies entirely on its output. In some cases, this might be an acceptable or even necessary form of coupling.

  • Math Libraries A sin() function relying on a radianToDegree() function from the same library
  • Middleware Pipeline A logging middleware function that relies on the output of a preceding authentication middleware
  • Data Transformation A data processing function relies on the specific output format of a data fetching function

Advantages of Tightly-Coupled Modules

  • Obtain only the data that is needed. E.g., parsing source code for a specific query
  • Directly use the data with no need to translate to/from a data format
  • Layers can be skipped that are not needed. E.g., ISO 7-level network model vs. TCP/IP
  • All the features of a specific API can be used. E.g., writing code for MySQL instead of a generic database format (ODBC)
  • Decoupling leads to abstractions, which can make the programming task more difficult than direct use
  • In general, tightly-coupled modules can be more efficient at the cost of flexibility