Code Smell 121 — String Validations
You need to validate strings. So you don’t need strings at all
TL;DR: Search for missing domain objects when validating strings.
Problems
- Primitive obsession.
- Bijection Fault
- Validated strings are a subset of all possible strings.
- Fail Fast principle violation.
- Single Responsibility Principle violation.
- DRY Principle Violation.
Solutions
- Create a first-class object representing the concept under the MAPPER
Context
Serious software has lots of string validations.
Often, They are not in the correct places.
This leads to non-robust and corrupt software.
The simple solution is to build only real-world and valid abstractions.
Sample Code
Wrong
<?//First Example: Address Validation
class Address {
function __construct(string $emailAddress) {
//String validation on Address class violates SRP
$this->validateEmail($emailAdress);
//...
} private function validateEmail(string $emailAddress) {
$regex = "/[a-zA-Z0-9_-.+]+@[a-zA-Z0-9-]+.[a-zA-Z]+/";
//Regex is a sample / It might be wrong
//Emails and Urls should be first class objects if (!preg_match($regex, $emailAddress))
{
throw new Exception('Invalid email address ' . emailAddress);
}
}
}//Second Example: Wordleclass Wordle {
function validateWord(string $wordleword){
//Wordle word should be a real world entity. Not a subset of strings
}
}
Right
<?//First Example: Address Validation
class Address {
function __construct(EmailAddress $emailAddress) {
//Email is always valid / Code is cleaner
//...
}
}class EmailAddress {
//We can reuse this object many times avoiding copypasting
string $address;
private function __construct(string $emailAddress) {
$regex = "/[a-zA-Z0-9_-.+]+@[a-zA-Z0-9-]+.[a-zA-Z]+/";
//Regex is a sample / It might be wrong
//Emails and Urls are first class objects if (!preg_match($regex, $emailAddress))
{
throw new Exception('Invalid email address ' . emailAddress);
}
$this->address = $emailAddress;
}
}//Second Example: Wordleclass Wordle {
function validateWord(WordleWord $wordleword){
//Wordle word is a real world entity. Not a subset of string
}
}class WordleWord {
function __construct(string $emailAddress)
//Avoid building invalid world words
//For example length != 5
}
}
Detection
[X] Semi-Automatic
We can check all constructors validating strings and reify the missing concepts.
Tags
- Primitive Obsession
Conclusion
Small objects are hard to find.
Primitive obsessors always complain about this kind of indirections.
Creating these new small concepts keeps our model loyal to the bijection and ensures our models are always healthy.
Relations
More Info
Credits
Photo by Brett Jordan on Unsplash
Less than 10% of the code has to do with the ostensible purpose of the system; the rest deals with input-output, data validation, data structure maintenance, and other housekeeping.
Mary Shaw
This article is part of the CodeSmell Series.