Refactoring 012 – Convert Your Key/value Into Full Behavioral Objects

Converting your anemic dictionaries is easy

TL;DR: Convert your key/value into full behavioral objects

Problems Addressed ๐Ÿ˜”

Related Code Smells ๐Ÿ’จ

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-vi-cmj31om?embedable=true

Context ๐Ÿ’ฌ

You have anemic associative arrays that hold unstructured data.

You want richer objects with stricter controls.

Static typed languages can also add type checking to these objects.

Steps ๐Ÿ‘ฃ

  1. Find the references to the object or associative array
  2. Reify it
  3. Replace generic calls with setters and getters for every key.

You can also debug them better this way.

  1. Add parameter and return type hinting to interfaces.

Do this if your language supports it.

  1. Add stronger assertions on the setters between different keys.

(if you are using TCR, you can do baby refactoring steps)

Sample Code ๐Ÿ’ป

Before ๐Ÿšจ

<?

class AuthenticationHelper extends Singleton {

  private $data = [];

  function setParameter(string $key, ?$value) {
    // no type checking
    // value as the name is too generic
    // Since SOME parameters might be null
    // You can't check a single parameter for not null

    $this->data[$key] = value;
  }

  function getParameter(string $key) {
    // no return type hinting
    return $this->data[$key] ?? null;
  }

}

// Usages

AuthenticationHelper::getInstance
  ->setParameter('oauth2_token', []);
// type error not caught

AuthenticationHelper::getInstance
  ->setParameter('scopes', null);
// We need to enforce this not to be NULL

AuthenticationHelper::getInstance
  ->setParameter('user', 'Elon');
// This should not mutate
// No validation with business rules

$credential =
  AuthenticationHelper::getInstance
    ->getParameter('oauth2token');
// Typo not detected

// You can not easily find
// references to methods setting the oauth2_token

After ๐Ÿ‘‰

<?

class AuthenticationCredentials {

  private $user;
  private $oauth2_token;

  function __construct(User $user) {
    $this->validateUser($user);
    // Specific validation rules

    $this->user = $user;
    // Can't mutate 
  }

  function oauth2_token(string $token): void {
    // You can add specific validations
    $this->oauth2_token = $token;
  }

  function oauth2_token(): string {    
    // Return type hinting
    return $this->oauth2_token;
  }

}

// Usages

$credentials = new AuthenticationCredentials(new User('Elon'));
// Valid since creation

$credentials->oauth2_token([]);
// type errors are caught

$credentials->oauth2_token(null);
// can't be null. Fail fast

$credentials->scope();
// Typo detected

Now, you have an anemic data class.

Also known as aย DTO.

It is time to give it behavior.

You can also remove some getters and setters.

Type ๐Ÿ“

[X] Semi-Automatic

You can perform this refactor with the aid of an IDE.

Safety ๐Ÿ›ก๏ธ

This is not an automatic refactoring.

Small steps are safe if you have good coverage.

Why is the Code Better? โœจ

Your new object fails fast and is more declarative.

You can debug it easily and find the referencing methods.

How Does it Improve the Bijection? ๐Ÿ—บ๏ธ

An associative array is a generic container with no real-world meaning.

Its keys are strings that could hold anything, valid or not.

A reified object gives each key a name, a type, and a purpose.

Code should map to the real world, following theย Bijectionย principle.

Every concept in the domain needs a counterpart in the code.

That is the core idea behind theย MAPPER.

The new object’s name and methods describe what it represents.

Limitations โš ๏ธ

Dynamically typed languages can’t enforce type or domain restrictions.

This applies to the values inside the object.

Tags ๐Ÿท๏ธ

  • Anemic Models

Level ๐Ÿ”‹

[X] Beginner

Related Refactorings ๐Ÿ”„

https://hackernoon.com/improving-the-code-one-line-at-a-time?embedable=true

Refactor with AI ๐Ÿค–

Suggested Prompt: 1. Find the references to the object or associative array.2. Reify it into a full object.3. Replace generic calls with setters and getters for every key.4. Add parameter and return type hinting to interfaces.5. Add stronger assertions between different keys.

Without Proper Instructions ๐Ÿ“ต

With Specific Instructions ๐Ÿ‘ฉโ€๐Ÿซ

Conclusion ๐Ÿ

Associative arrays are convenient until they grow into hidden domain models.

Once several parts of your code read and write the same keys, reify them.

A dedicated object turns implicit rules into explicit, testable behavior.

You gain type safety, fail-fast validation, and traceable references.

See also ๐Ÿ“š

Wikipedia: Reification

https://refactoring.guru/replace-data-value-with-object?embedable=true

Credits ๐Ÿ™

Image byย MustangJoeย fromย Pixabay


This article is part of the Refactoring Series.

https://maximilianocontieri.com/how-to-improve-your-code-with-easy-refactorings

Leave a Comment

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