Refactoring Task
Overview
This exercise addresses a design where a new business logic connects directly to a legacy file-saving API.
The goal is to analyze this structure from the perspectives of extensibility, maintainability, readability, and testability, and consider abstraction through an Adapter and possible redesign.
Initial Code
In the following example, the ExportService
directly depends on LegacyFileSaver
, forcefully adapting data to match its interface.
As a result, the new code is burdened with format conversions and compatibility logic, leading to poor separation of concerns.
- TypeScript
- PHP
- Python
// レガシーAPI(旧仕様)
class LegacyFileSaver {
saveData(data: string) {
console.log(`保存形式: <<<${data}>>>`);
}
}
class ExportService {
exportReport(json: object) {
const legacySaver = new LegacyFileSaver();
const formatted = JSON.stringify(json)
.replaceAll("{", "[")
.replaceAll("}", "]");
legacySaver.saveData(formatted);
}
}
<?php
// レガシーAPI(旧仕様)
class LegacyFileSaver {
public function saveData(string $data): void {
echo "保存形式: <<<{$data}>>>\n";
}
}
class ExportService {
public function exportReport(array $json): void {
$legacySaver = new LegacyFileSaver();
$formatted = str_replace(['{', '}'], ['[', ']'], json_encode($json));
$legacySaver->saveData($formatted);
}
}
# レガシーAPI(旧仕様)
class LegacyFileSaver:
def save_data(self, data: str):
print(f"保存形式: <<<{data}>>>")
class ExportService:
def export_report(self, json_obj: dict):
legacy_saver = LegacyFileSaver()
formatted = str(json_obj).replace("{", "[").replace("}", "]")
legacy_saver.save_data(formatted)
Question 1: What are the design issues in this code?
List and explain the structural issues using the following points as guidance:
- Tight coupling with the legacy API (
LegacyFileSaver
), making reuse and replacement difficult - Responsibility confusion due to format conversions and compatibility logic being mixed in the new code
- The caller handles interface mismatches directly, breaking decoupling principles
- Low testability due to dependency on real environments or legacy APIs
Question 2: How can this be refactored into a more flexible and maintainable structure?
Organize your improvement strategy around the following points:
- How to abstract away the legacy API so that the new design doesn’t need to be aware of its details
- How to make the system adaptable to alternative saving mechanisms (e.g., cloud storage, database)
- How to invert dependencies and enable implementation swapping via interfaces
Example: Candidate Design Patterns
Pattern Name | Purpose and Effect |
---|---|
Adapter | Wraps the legacy API and adapts it to be compatible with the new design |
Facade | Provides a simplified interface for multiple legacy API calls |
Strategy | Allows switching between different saving logics, supporting both legacy and new |
Bridge | Separates abstraction from implementation to allow flexible changes in storage type |
Optional Extensions
- If modern APIs (e.g., REST or cloud storage) are introduced alongside legacy ones, how should the structure evolve?
- If a common interface for saving logic is introduced, what responsibilities should be extracted and separated?
Suggested Output Format (for review or team discussion)
- List of structural issues (minimum three)
- Refactoring strategy and rationale
- Proposed pattern(s) and design benefits
- (Optional) Simplified class/interface diagram showing the refactored structure