Refactoring 013 – Eliminating Repeated Code with DRY Principles

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 46 – Repeated Code

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

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.