I recently had a back and forth over at StackOverflow about my answer to this question.
The question was simple. The author wanted to transform a number into an abbreviated version that appended a K, M, or B depending on the size of the number.
One user suggested a very straightforward and naive answer:
function round_thousands($number){
if($number < 1000){
return $number;
} else {
return number_format($number/1000, 1).'K';
}
}
Ignoring the fact that the name of the function doesn't match what it does, and that it only converts into K, I can see why this answer might be attractive at first.
However, I elected to take a more robust approach, and create an interface:
interface Quantifier {
public function quantify($value);
}
I then created a default quantifier called NumberQuantifier:
class NumberQuantifier implements Quantifier {
protected $quantifierList;
public function __construct($quantifierList) {
$this->quantifierList = $quantifierList;
arsort($this->quantifierList); //Make sure they are largest too smallest.
}
public function quantify($number) {
foreach ($this->quantifierList as $symbol => $threshold) {
if ($threshold > $number) continue;
return number_format($number / $threshold, 1) . $symbol;
}
}
}
Simple stuff, I thought.
This allows you to create quantifiers for all sorts of things: file size, mass, length, volume, etc. Without having to copy/paste a new function every time:
$numberQuantifier = new NumberQuantifier(array(
'B' => 1000000000,
'M' => 1000000,
'K' => 1000
));
Furthermore, in the event that you had a more advanced case, you could implement Quantifier and create some custom functionality for quantifying.
However, I was soon met with a critic who insisted that I shouldn't be using a class for this at all. In fact, he was adamant, despite my numerous attempts to satisfy why a class would be ideal.
Argument
His argument was that a class
- Must have some mutable state
- Must encapsulate more than a single operation
Otherwise, he contends, it should be a global function.
His alternative (I think) would be to do something like:
function number_quantify($value, $quanitfierList) {
foreach ($quantifierList as $symbol => $threshold) {
if ($threshold > $number) continue;
return number_format($number / $threshold, 1) . $symbol;
}
}
function quantify_mass($value) {
$quantifieryList = array(); //quantifiers here.
return number_quantify($value, $quantifierList);
}
function quantify_length($value) { //.. }
//etc.
Basically, for any type of quantifier, you have to create a new function that would always live in global space. Alternatively, you could manually pass the $quantifierList
to each function call of number_quantify
which seems like a maintenance nightmare.
I believe that my use of an interface and polymorphism not only makes the code cleaner and more maintainable, but also would facilitate better unit testing.
Question
Am I wrong? Is having a class that encapsulates a single operation and no mutable state some type of code smell?