Don't Repeat Yourself
TL;DR: How to remove repeated code
Problems Addressed
- Don't Repeat Yourself
- Copy/Paste Programming
- No single source of truth
Related Code Smells
Code Smell 11 - Subclassification for Code Reuse
Code Smell 232 - Reusable Code
Context
Duplicated code is a severe code smell; it leads to maintainability problems and ripple effects.
Start by identifying behavior duplication.
Once you find it, you will extract it into reusable functions or classes, reducing redundancy, creating a single source of truth, and simplifying future updates.
Behavior duplication is a sign of a missing abstraction you need to create.
As always, you should search for it in the real world.
Refactoring isn't a one-time event; it's an ongoing process that should be integrated into your development workflow.
Steps
- Make a contextual copy of the repeated code
- Parametrize what is different
- Invoke the abstraction
- Find a real-world metaphor for the abstraction
(This is the harder and not mechanical step)
Sample Code
(This is actual code generated by Google Gemini)
See a complete explanation in this talk
Before
<?php
class AccessControlPanel {
private $users = [];
public function createRegularUser($username, $password, $email) {
$user = [
"username" => $username,
"email" => $email,
"type" => $this->regularUserRole(),
"creationDate" => $this->timeSource->currentTimestamp(),
"needsToChangePassword" = $this->needsToChangePassword(),
"loginPolicy" => $this->userLoginPolicy()
]
$this->users[] = $user;
$this->addCreationToJournal($user);
}
public function createAdminUser($username, $password, $email) {
$user = [
"username" => $username,
"email" => $email,
"type" => $this->regularUserRole(),
"creationDate" => $this->timeSource->currentTimestamp(),
"needsToChangePassword" = $this->needsToChangePassword(),
"loginPolicy" => $this->adminUserLoginPolicy()
]
$this->users[] = $user;
$this->addCreationToJournal($user);
return $user;
}
}
?>
After
<?php
class AccessControlPanel {
private $users = [];
// 1. Make a contextual copy of the repeated code
private function createUser(
$username,
$password,
$email,
$role,
$loginPolicy) {
$user = [
"username" => $username,
"email" => $email,
"type" => $role,
"creationDate" => $this->timeSource->currentTimestamp(),
"needsToChangePassword" => $this->needsToChangePassword(),
"loginPolicy" => $loginPolicy
];
$this->users[] = $user;
$this->addCreationToJournal($user);
return $user;
}
// 2. Parametrize what is different (in this case $role and $loginPolicy)
public function createRegularUser($username, $password, $email) {
// 3. Invoke the abstraction
return $this->createUser(
$username,
$password,
$email,
$this->regularUserRole(),
$this->userLoginPolicy());
}
public function createAdminUser($username, $password, $email) {
return $this->createUser(
$username,
$password,
$email,
$this->adminUserRole(),
$this->adminUserLoginPolicy());
}
// 4. Find a real-world metaphor for the abstraction
// private function createUser(
// $username,
// $password,
// $email,
// $role,
// $loginPolicy)
}
?>
Type
- [x]Semi-Automatic
The steps are defined but sometimes not about text duplication, but behavior duplication.
Safety
Since this is not a mechanical refactoring, you need good coverage on the code you modify.
Why is the code better?
You have a single source of truth, more compact code, and an easier-to-maintain solution.
Tags
- Coupling
Related Refactorings
https://hackernoon.com/improving-the-code-one-line-at-a-time?embedable=true
https://maximilianocontieri.com/refactoring-007-extract-class?embedable=true
https://maximilianocontieri.com/refactoring-010-extract-method-object?embedable=true
This article is part of the Refactoring Series.
https://maximilianocontieri.com/how-to-improve-your-code-with-easy-refactorings?embedable=true