Cohesion measures how strongly related and focused the responsibilities within a module (class, function, or package) are. High cohesion means the module does one thing well; low cohesion means it does many unrelated things.
Core Concept
Cohesion answers the question: “How well do the parts of this module belong together?”
- High Cohesion: Elements work together toward a single, well-defined purpose
- Low Cohesion: Elements are loosely related or unrelated, serving multiple purposes
Why Cohesion Matters
Understandability
High cohesion makes code easier to understand:
- One focused responsibility per module
- Clear purpose and boundaries
- Predictable behavior
Maintainability
Changes are localized and less risky:
- Related functionality grouped together
- Modifications affect one focused area
- Fewer unexpected side effects
Reusability
Focused modules are more reusable:
- Single purpose means fewer dependencies
- Clear interface with minimal assumptions
- Can be extracted and used elsewhere
Levels of Cohesion
From lowest (worst) to highest (best):
1. Coincidental Cohesion (Worst)
Elements grouped arbitrarily with no meaningful relationship:
// BAD: Random unrelated methods
public class Utilities
{
public void SendEmail(string to, string body) { }
public decimal CalculateTax(decimal amount) { }
public void LogMessage(string message) { }
public string FormatDate(DateTime date) { }
}2. Logical Cohesion
Elements perform similar operations but on different types of data:
// BAD: Grouped by similarity, not purpose
public class InputHandler
{
public void Handle(string type, object data)
{
switch (type)
{
case "file": HandleFile((string)data); break;
case "network": HandleNetwork((Stream)data); break;
case "user": HandleUser((UserInput)data); break;
}
}
}3. Temporal Cohesion
Elements grouped because they execute at the same time:
// BAD: Grouped by "when" not "what"
public class Startup
{
public void Initialize()
{
ConnectDatabase();
LoadConfiguration();
StartWebServer();
InitializeLogging();
}
}4. Procedural Cohesion
Elements grouped because they follow a sequence:
// MEDIOCRE: Better than temporal, but still procedural
public class OrderProcessor
{
public void ProcessOrder()
{
ValidateOrder();
CalculateTotal();
ChargeCustomer();
UpdateInventory();
SendConfirmation();
}
}5. Communicational Cohesion
Elements operate on the same data:
// GOOD: Operates on same data
public class CustomerReport
{
private Customer _customer;
public string GetName() => _customer.Name;
public string GetAddress() => _customer.Address;
public decimal GetTotalPurchases() => _customer.CalculateTotal();
}6. Sequential Cohesion
Output of one element is input to the next:
// GOOD: Clear data flow
public class TextProcessor
{
public string Process(string input)
{
string trimmed = Trim(input);
string normalized = Normalize(trimmed);
string validated = Validate(normalized);
return validated;
}
}7. Functional Cohesion (Best)
All elements contribute to a single, well-defined task:
// EXCELLENT: Single focused responsibility
public class EmailValidator
{
public bool IsValid(string email)
{
return HasAtSign(email)
&& HasDomain(email)
&& HasValidFormat(email);
}
private bool HasAtSign(string email) => email.Contains("@");
private bool HasDomain(string email) => email.Split('@')[1].Contains(".");
private bool HasValidFormat(string email) => /* validation logic */;
}Achieving High Cohesion
Single Responsibility Principle
Single Responsibility Principle directly addresses cohesion:
// BAD: Multiple responsibilities (low cohesion)
public class UserService
{
public void CreateUser(User user) { }
public void SendWelcomeEmail(User user) { }
public void GenerateReport(DateTime start, DateTime end) { }
public void CleanupOldRecords() { }
}
// GOOD: Single responsibility per class (high cohesion)
public class UserRepository
{
public void CreateUser(User user) { }
public User GetUser(int id) { }
}
public class EmailService
{
public void SendWelcomeEmail(User user) { }
}
public class ReportGenerator
{
public Report GenerateUserReport(DateTime start, DateTime end) { }
}Feature-Based Organization
Group by feature, not by technical layer:
// BAD: Low cohesion - organized by type
/Controllers/UserController.cs
/Controllers/OrderController.cs
/Services/UserService.cs
/Services/OrderService.cs
/Models/User.cs
/Models/Order.cs
// GOOD: High cohesion - organized by feature
/Users/UserController.cs
/Users/UserService.cs
/Users/User.cs
/Orders/OrderController.cs
/Orders/OrderService.cs
/Orders/Order.csCohesion Order Refactoring
Cohesion Order technique from tidyings:
// BEFORE: Low cohesion - scattered responsibilities
public class ShoppingCart
{
public void AddItem(Item item) { }
public void CalculateShipping() { }
public void AddDiscount(Discount d) { }
public void CalculateTax() { }
public void RemoveItem(Item item) { }
public decimal GetTotal() { }
}
// AFTER: High cohesion - grouped by concern
public class ShoppingCart
{
// Item management
public void AddItem(Item item) { }
public void RemoveItem(Item item) { }
// Price calculations
public void CalculateTax() { }
public void CalculateShipping() { }
public void AddDiscount(Discount d) { }
public decimal GetTotal() { }
}
// EVEN BETTER: Extract separate responsibilities
public class ShoppingCart
{
private CartItems _items;
private PriceCalculator _pricing;
public void AddItem(Item item) => _items.Add(item);
public void RemoveItem(Item item) => _items.Remove(item);
public decimal GetTotal() => _pricing.Calculate(_items);
}Measuring Cohesion
LCOM (Lack of Cohesion of Methods)
Measures how many method pairs don’t share instance variables:
// High LCOM (low cohesion)
public class BadClass
{
private int _fieldA;
private int _fieldB;
public void Method1() { _fieldA++; } // Uses only fieldA
public void Method2() { _fieldB++; } // Uses only fieldB
// Methods don't share data - low cohesion!
}
// Low LCOM (high cohesion)
public class GoodClass
{
private int _fieldA;
private int _fieldB;
public void Method1() { _fieldA++; _fieldB++; }
public void Method2() { _fieldA--; _fieldB--; }
// Methods share data - high cohesion!
}Code Smell Indicators
Signs of low cohesion:
- Large classes: Too many responsibilities
- Long methods: Doing too many things
- Feature envy: Methods using other classes’ data more than their own
- Shotgun surgery: Changes require modifications across many methods
Cohesion vs Coupling
Cohesion and coupling work together:
| Aspect | High Cohesion | Low Coupling |
|---|---|---|
| Focus | Internal to module | Between modules |
| Goal | Related elements together | Independent modules |
| Result | Easy to understand | Easy to change |
The Sweet Spot: High cohesion within modules, low coupling between modules
// IDEAL: High cohesion + Low coupling
public class Order
{
private OrderItems _items;
private OrderPricing _pricing;
// High internal cohesion - all about order processing
public void AddItem(Item item) => _items.Add(item);
public decimal CalculateTotal() => _pricing.Calculate(_items);
}
// Low coupling - Order doesn't know about payment processing
public class PaymentProcessor
{
public void ProcessPayment(Order order, PaymentMethod method)
{
decimal amount = order.CalculateTotal();
// Process payment
}
}Common Pitfalls
God Classes
Classes that do everything (extremely low cohesion):
// BAD: God class
public class Application
{
public void Start() { }
public void HandleUserInput() { }
public void ProcessData() { }
public void RenderUI() { }
public void ManageDatabase() { }
public void SendEmails() { }
// Hundreds of methods...
}Utility Classes
Collections of unrelated helper methods:
// BAD: Low cohesion utility class
public static class Utils
{
public static string FormatDate(DateTime d) { }
public static void Log(string msg) { }
public static int CalculateAge(DateTime birth) { }
public static void SendEmail(string to) { }
}Better approach - create focused classes:
public class DateFormatter { }
public class Logger { }
public class AgeCalculator { }
public class EmailSender { }Package-Level Cohesion
Cohesion principles also apply at the package/component level. Robert C. Martin defines three Package Cohesion Principles:
Reuse-Release Equivalence Principle (REP)
“The granule of reuse is the granule of release.”
If classes are reused together, they should be versioned and released together. This ensures users can depend on stable, trackable versions.
Common Reuse Principle (CRP)
“The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.”
Group classes that are typically used together. Don’t force users to depend on classes they don’t need—this creates unnecessary coupling.
Common Closure Principle (CCP)
“The classes in a component should be closed together against the same kinds of changes.”
This is the Single Responsibility Principle applied to packages. Classes that change for the same reasons should be in the same package, minimizing the impact of changes.
The Tension Diagram
These three principles create competing forces:
- REP and CCP are inclusive (make packages larger)
- CRP is exclusive (makes packages smaller)
Early in development, favor CCP (developability). As systems mature and are reused, shift toward REP and CRP.
See Package Principles for detailed coverage including the tension diagram.
Resources
See Also
- Coupling - The complementary concept
- Package Principles - Cohesion principles at the package level (REP, CRP, CCP)
- Single Responsibility Principle - Enforces high cohesion
- Cohesion Order - Refactoring technique
- Object-Oriented Design
- Design Concepts
- Good Software Practices