Refactoring 013 - Eliminating Repeated Code with DRY Principles

cover
17 Jun 2024

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

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

  1. Make a contextual copy of the repeated code
  2. Parametrize what is different
  3. Invoke the abstraction
  4. 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

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