Converting your anemic dictionaries is easy
TL;DR: Convert your key/value into full behavioral objects
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.
You can also debug them better this way.
Do this if your language supports it.
(if you are using TCR, you can do baby refactoring steps)
<?
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
<?
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.
[X] Semi-Automatic
You can perform this refactor with the aid of an IDE.
This is not an automatic refactoring.
Small steps are safe if you have good coverage.
Your new object fails fast and is more declarative.
You can debug it easily and find the referencing methods.
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.
Dynamically typed languages can't enforce type or domain restrictions.
This applies to the values inside the object.
[X] Beginner
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.
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.
Image by MustangJoe from Pixabay
This article is part of the Refactoring Series.
https://maximilianocontieri.com/how-to-improve-your-code-with-easy-refactorings