Refactoring Task
Overviewβ
In this exercise, you will analyze a Notifier
class implementation in which an external dependency (Mailer
) is instantiated with new
each time it is needed.
Identify the design issues this introduces, and propose refactoring strategies that improve flexibility and testability.
Initial Codeβ
The following code defines a Notifier
class responsible for sending notifications to users.
It directly instantiates the Mailer
internally, which works initially, but could lead to maintainability and extensibility problems over time.
- TypeScript
- PHP
- Python
class Mailer {
send(to: string, message: string) {
console.log(`γ‘γΌγ«ιδΏ‘: ${to} => ${message}`);
}
}
class Notifier {
notify(userId: string, content: string) {
const mailer = new Mailer();
const formatted = `[ιη₯] ${content}`;
mailer.send(`${userId}@example.com`, formatted);
}
}
<?php
class Mailer {
public function send(string $to, string $message): void {
echo "γ‘γΌγ«ιδΏ‘: {$to} => {$message}\n";
}
}
class Notifier {
public function notify(string $userId, string $content): void {
$mailer = new Mailer();
$formatted = "[ιη₯] {$content}";
$mailer->send("{$userId}@example.com", $formatted);
}
}
class Mailer:
def send(self, to: str, message: str):
print(f"γ‘γΌγ«ιδΏ‘: {to} => {message}")
class Notifier:
def notify(self, user_id: str, content: str):
mailer = Mailer()
formatted = f"[ιη₯] {content}"
mailer.send(f"{user_id}@example.com", formatted)
Question 1: What are the design issues in this code?β
List and explain the design problems based on the following aspects:
- Testability (Is it possible to replace
Mailer
for testing?) - Flexibility (What if the type of mailer needs to be changed?)
- Dependency management (Is
Notifier
too tightly coupled toMailer
?) - Conformance to design principles (e.g., the Dependency Inversion Principle)
Question 2: How should the design be improved?β
Propose improvements based on the following considerations:
- How can
Notifier
be decoupled from the internal instantiation ofMailer
? - How can the
Mailer
be replaced during unit testing? - What benefits arise from applying Dependency Injection?
Example: Design Pattern Candidatesβ
Pattern Name | Purpose and Effects |
---|---|
Factory Method | Extracts object creation logic and allows switching implementations as needed |
Abstract Factory | Provides a group of related objects (e.g., Mailer , Logger ) as a unified interface |
Builder | Separates construction steps and enables flexible, step-by-step initialization |
Singleton | Ensures only one instance exists globally and is reused across the system |
Dependency Injection | Injects dependencies externally for better flexibility, testability, and decoupling |
Optional Extensionsβ
- If notification methods include
Slack
orLINE
in addition to email, how would you adapt the design? - If the test environment requires a mock mailer that logs instead of sending, how would you design for this flexibility?
Suggested Output Format (for review or study groups)β
- List of at least three structural problems
- Refactoring approach and the reason for choosing specific patterns
- Design rationale and improvements (with optional sketches or pseudocode)