Tight Coupling
Descriptionโ
What does it look like?โ
- A class (e.g.,
OrderService
) directly instantiates or calls another class (e.g.,EmailService
,InventoryService
) - Due to strong dependencies, any change to a dependency immediately affects the caller
- Difficult to substitute mocks or stubs during testing
- Readability, flexibility, and testability suffer
Why is it a problem?โ
- Every addition or modification requires changes in the original class, resulting in high maintenance cost
- Leads to a state where โit works, so donโt touch it,โ accumulating technical debt
- The class ends up with multiple responsibilities, resulting in poor separation of concerns
Bad Example of the Anti-patternโ
- TypeScript
- PHP
- Python
class Mailer {
send(email: string, content: string) {
console.log(`้ไฟกใใพใใ: ${email} โ ${content}`);
}
}
class UserService {
private mailer: Mailer;
constructor() {
this.mailer = new Mailer(); // โ ใฏใฉในๅ
ใง็ดๆฅ็ๆ๏ผๅฏ็ตๅ๏ผ
}
notifyUser(email: string) {
const content = "ใใใใ๏ผ";
this.mailer.send(email, content); // โ Mailer ใฎๅญๅจใ็ฅใใใใฆใใ
}
}
<?php
class Mailer {
public function send(string $email, string $content): void {
echo "้ไฟกใใพใใ: {$email} โ {$content}\n";
}
}
class UserService {
private Mailer $mailer;
public function __construct() {
$this->mailer = new Mailer(); // โ ๅฏ็ตๅ๏ผ็ดๆฅ็ๆ
}
public function notifyUser(string $email): void {
$content = "ใใใใ๏ผ";
$this->mailer->send($email, $content); // โ Mailer ใฎๅญๅจใ็ฅใใใใฆใใ
}
}
// ๅฉ็จไพ
$service = new UserService();
$service->notifyUser("user@example.com");
class Mailer:
def send(self, email: str, content: str):
print(f"้ไฟกใใพใใ: {email} โ {content}")
class UserService:
def __init__(self):
self.mailer = Mailer() # โ ๅฏ็ตๅ๏ผ็ดๆฅ็ๆ
def notify_user(self, email: str):
content = "ใใใใ๏ผ"
self.mailer.send(email, content) # โ Mailer ใซๅผทใไพๅญ
# ๅฉ็จไพ
service = UserService()
service.notify_user("user@example.com")
Issues:โ
UserService
is tightly coupled toMailer
, and locked into its specific implementation- Difficult to switch to another mail system (e.g., external API, mock service)
- If requirements change (e.g., logging mailer, switching to SMS), changes must be made to
UserService
- Even if you want to share the
Mailer
logic, each instance is created separately, making it hard to centralize configuration
Refactoring by Patternโ
Design patterns that can address thisโ
Pattern | Overview | Main Refactoring Approach |
---|---|---|
Observer | Delegate processing via event notification | Use external registration for loose coupling |
Mediator | A central coordinator mediates communication | Reduce many-to-many dependencies to 1-to-1 |
Dependency Injection (DI) | Inject dependencies from outside for easy replacement | Improves testability and extensibility |