Introduction
Now we know:
- Abstraction helps us manage complexity
- Modularity helps us organize software
- Separation of Concerns helps us divide responsibilities
However, even if we successfully separate our system into modules, another critical appears:
How much should these modules know about each other?Imagine two software systems.
System A:
User Module
|
v
Order Module
|
v
Payment Module
|
v
Inventory ModuleSystem B:
User Module <------> Payment Module
^ \ / ^
| \ / |
| \ / |
v \ / |
Order Module <--> Inventory
^ /
| /
+------------------+Both systems may work.
But one is dramatically harder to maintain.
Why?
Coupling and Cohesion
These concepts are so important that nearly every software engineering principle, design pattern, architecture style, and framework eventually boils down to improve one or both.
In fact:
Good software design is largely the art of achieving low coupling and high cohesion.
Everything else is often a consequence.
If abstraction helps us reduce complexity, and modularity helps us organize complexity.
then coupling determines:
How complexity spreads through a system.
Historical Context
During the Software Crisis (1960s â 1980s), engineers noticed something strange.
Two systems with similar functionality could have drastically different maintenance costs.
Example:
System A:
100,000 lines
System B:
100,000 linesSame size.
But:
System A: Easy to modify
System B: Extremely difficult to modifyWhy?
Researchers began studying software architecture.
They discovered two characteristics repeatedly appeared in successful systems:
LOW COUPLING
HIGH COHESIONThese became fundamental software engineering principles.
Understanding Coupling
What Is Coupling?
Coupling measures:
How strongly one component depends on another component.
Or more simply:
The degree of dependency between two software components.
Simplified:
How much one component knows about another component.
Real-World Analogy: Train Cars
Imagine two apartments.
Loosely Coupled
Neighbors:
Apartment A
Apartment BThey can live independently.
One family moves out.
The other continues normally.
Tightly Coupled
Imagine:
Apartment A shares:
Water
Electricity
Kitchen
Bathroom
with
Apartment BNow any change affects both apartments.
This is tight coupling.
Software behaves similarly.
Formal Definition
Coupling is:
The degree of dependency between software modules.
Higher dependency:
Higher CouplingLower dependency:
Lower CouplingWhy Coupling Exists
An important truth:
Coupling is unavoidable.
No useful system consists of completely independent parts.
Example:
Order Modulemust interact with:
Payment ModuleOtherwise orders cannot be paid.
The goal is NOT:
Zero Coupling
Impossible.
The goal is:
Manageable Coupling
Why Coupling Matters
Every dependency introduces risk.
Example:
class OrderService
{
private:
PaymentService paymentService;
};OrderService depends on PaymentService.
Therefore:
OrderService
â
Depends On
â
PaymentServiceThis dependency creates coupling.
Visualization Coupling
Low Coupling:
+---------+
| ModuleA |
+---------+
+---------+
| ModuleB |
+---------+
+---------+
| ModuleC |
+---------+Independent modules.
High Coupling:
A â B â C â D â E
â â
+---------------+Complex dependency network.
Changes become dangerous.
Example: Tight Coupling
Consider:
class MySQLDatabase
{
public:
void save()
{
}
};class UserService
{
private:
MySQLDatabase database;
public:
void registerUser()
{
database.save();
}
};Problem:
UserService depends directly on MySQL.Suppose tomorrow:
MySQL --> PostgreSQLNow UserService must change.
This is tight coupling.
A Mental Model
Thinking of coupling as a rope.
Loose Coupling:
Module A
|
|
Module BSmall connection.
Tight Coupling:
Module A
|||||||||||||||
Module BMany connections, Harder to separate.
Tight Coupling
Definition:
A component depends heavily on the internet details of another component.
Example:
class PaymentService
{
public:
void connectGateway();
void createRequest();
void sendRequest();
void parseResponse();
};Client:
PaymentService payment;
payment.connectGateway();
payment.createRequest();
payment.sendRequest();
payment.parseResponse();The client knows too much.
Problems:
Fragile
Hard to Change
Hard to Test
Hard to ReplaceLoosely Coupling
Definition:
A component depends only on the essential behavior of another component.
Example:
class PaymentService
{
public:
void processPayment();
};Client:
payment.processPayment();Simple.
Only necessary knowledge exposed.
Benefits:
Easy to Change
Easy to Extent
Easy to Test
Easy to ReplaceSigns of Tight Coupling
When reviewing designs, look for:
Sign 1: Direct Object Creation
class OrderService
{
private:
PaymentService payment;
};OrderService is tied directly to PaymentService.
Sign 2: Knowledge of Internals
payment.connect();
payment.prepare();
payment.send();
payment.retry();Too much knowledge.
Sign 3: Ripple Effects
Change:
Payment ModuleBreaks:
Orders
Invoices
Reports
NotificationsStrong coupling exists.
Sign 4: Difficult Testing
Testing:
OrderServicerequires:
Real Payment Gateway
Real Database
Real Email ServiceTightly coupled system.
Better Design
Introduce abstraction.
class IDatabase {
public:
virtual void save() = 0;
};Implementation:
class MySQLDatabase: public IDatabase {
public:
void save() override
{
// ...
}
};Usage:
class UserService {
private:
IDatabase& database;
public:
UserService(IDatabase& db)
: database(db)
{
}
};Now:
UserService depends on abstractionCoupling decreases significantly.
Symptoms of High Coupling
Frequent Ripple Effects
Change:
Class AUnexpectedly affects:
Class B
Class C
Class D
Class EFear of Modification
Developers say:
Don't touch that code.Major warning sign.
Difficult Testing
Creating one object requires:
Databases
Network
Cache
Queue
LoggerToo many dependencies.
Difficult Reuse
Class cannot be reused because it depends on everything.
Types of Coupling
1: Content Coupling (Worst)
One module(component) directly modifies internals of another.
Example:
class User
{
public:
std::string password;
};External code:
user.password = "345";Very dangerous.
2: Common Coupling
Multiple modules share global data.
double globalTaxRate = 18;Used everywhere.
Problems:
Who changed it?
Who depends on it?Becomes difficult to reason about.
3: Control Coupling
One module control another's behavior through flags.
Example:
generateReport(true);Question:
What does true mean?Bad design.
Better:
generateSummaryReport();
generateDetailedReport();4: Data Coupling
Module exchange only necessary data.
processPayment(amount);Simple.
Clear.
Independent.
Desired Goal
Move toward:
LOW COUPLINGNot:
ZERO COUPLINGImportant distinction.
Some dependencies are necessary.
Coupling and Change
One of the most important ideas in software engineering:
The true cost of coupling appears during change.
Today:
System WorksTomorrow:
Requirements ChangeNow tightly coupled systems become expensive.
A famous observation:
Change propagates through dependencies.The more coupling:
The farther changes travel.Coupling and Reusability
Consider:
class UserService
{
private:
Database database;
EmailService email;
PaymentService payment;
};Can this class be reused elsewhere?
Difficult.
Too many dependencies.
Compare:
class UserValidator
{
public:
bool validate();
};Few dependencies.
Highly reusable.
Coupling vs Modularity
Modularity answers:
How is the system divided?Coupling answers:
How dependent are these divisions?Reducing Coupling
Professional designer constantly ask:
How can we reduce unnecessary dependencies?
Technique 1: Hide Implementation Details
Techniquen 2: Use Interaces
Technique 3: Depend on Abstractions
Technique 4: Separate Responsibilities
Technique 5: Minimize KnowledgeUnderstanding Cohesion
We learned:
Coupling measures the dependencies between modules.
Good software generally aims for:
Low Coupling
However, minimizing coupling alone does not produce good designs.
Consider this class:
class Utility
{
public:
void sendEmail();
void calculateTax();
void printInvoice();
void connectDatabase();
void generateReport();
};Notice something strange?
These methods are unrelated.
Even though the class may have low coupling with other classes, it still feels wrong.
Why?
Because it lacks: Cohesion
A famous software engineering principle says:
High Cohesion, Low Coupling
What Is Cohesion?
Cohesion measures:
How strongly related the responsibilities inside a module are.
Or:
How well things belong together
Or:
The degree to which the elements inside a module belong together.
Real-World Analogy: Toolbox
Good toolbox:
Hammer
Screwdriver
Wrench
PliersAll related. High cohesion.
Everything serves a common purpose: Construction and Repair.
Bad toolbox:
Hammer
Pizza
Laptop
Dog Leash
ToothbrushRandom items. Low cohesion.
Software works exactly the same way.
Formal Definition
Cohesion is:
The degree to which elements within a module belong together.
Higher relatedness:
Higher CohesionLower relatedness:
Lower CohesionExample: Low Cohesion
class EmployeeManager
{
public:
void addEmployee();
void calculateSalary();
void sendEmail();
void generateReport();
void connectDatabase();
void printInvoice();
};Question:
What is this class responsible for?
Answer:
EverythingLow cohesion.
Example: High Cohesion
class EmployeeService
{
public:
void addEmployee();
void removeEmployee();
void updateEmployee();
};Single responsibility. Strong cohesion.
Visualizing Cohesion
Low Cohesion:
EmployeeService
ââ Email
ââ Database
ââ Reports
ââ Payroll
ââ AuthenticationRandom responsibilities.
High Cohesion:
EmployeeService
ââ Add Employee
ââ Remove Employee
ââ Update EmployeeEverything belongs together.
The Core Idea
A module should have:
One Clear PurposeWhen someone asks:
Why does this class exist?Why Cohesion Matter
High cohesion improves:
Readability
Maintainability
Testability
ReusabilityBecause responsibilities are clear.
Cohesion Levels
Coincidental Cohesion
Random responsibilities grouped together.
Example:
Utils
Helpers
CommonStuff
MiscFunctionsclass Utility
{
public:
void print();
void login();
void calculateTax();
void connectDatabase();
};No meaningful relationship.
Worst form.
Logical Cohesion
Methods are similar but still not strongly related.
Example:
class InputHandler
{
handleKeyboard();
handleMouse();
handleTouch();
}Better, but still weak.
Functional Cohesion
Everything contributes toward one goal.
Example:
class PaymentProcessor
{
validatePayment();
authorizePayment();
capturePayment();
}Single purpose, Highest cohesion.
Cohesion and Responsibility
One of the easiest ways to evaluate cohesion:
Ask:
How many responsibilities does this class have?One responsibility:
High CohesionNotice how this sounds similar to:
Single Responsibility PrincipleExactly, SRP is essentially a practical application of cohesion.
Coupling vs Cohesion
| Coupling | Cohesion |
|---|---|
| Between modules | Within module |
| Dependency measurement | Responsibility measurement |
| Want low | Want high |
| Affects flexibility | Affects clarity |
The Golden Rule
The fundamental design goal:
LOW COUPLING
HIGH COHESIONThis combination creates maintainable systems.
Expert Notes
One of the strongest heuristics in software design:
Things that change together should live together.This increases cohesion.
Another:
Things that change independently should be separated.This decreases coupling.
A useful mental model:
Cohesion pulls things together.
Coupling pushes things apart.Good design balances both forces.
Most software design principles you will learn later:
SRP
OCP
ISP
DIP
Composition
Design Patternsare largely mechanisms for improving coupling and cohesion.
Leave a comment
Your email address will not be published. Required fields are marked *
