Yet more code smells? More? Where do they come from?
We see several symptoms and situations that make us doubt the quality of our development.
Let's look at some possible solutions.
Most of these smells are just hints of something that might be wrong. They are not rigid rules.
Let's continue...
We want our code to behave different on different environments, operating systems, so taking decisions at compile time is the best decision, isn't it?.
#if VERBOSE >= 2
printf("trace message");
#endif
if (runtimeEnvironment->traceDebug()){
printf("trace message");
}
## even better with polymorphism and we avoid annoying ifs
runtimeEnvironment->traceDebug("trace message");
This is a syntactic directive promoted by several languages, therefore it is easy to detect and replace with real behavior.
Adding an extra layer of complexity makes debugging very difficult. This technique was used when memory and CPU were scarce. Nowadays, we need clean code, and we must leave premature optimization buried in the past.
Bjarne Stroustrup, in his book The Design and Evolution of C++, regrets on the pre-processor directives he created years before.
C++ is designed to allow you to express ideas, but if you don't have ideas or don't have any clue about how to express them, C++ doesn't offer much help.
Bjarne Stroustrup
sort, sortOld, sort20210117, workingSort, It is great to have them all. Just in case
findMatch()
findMatch_new()
findMatch_newer()
findMatch_newest()
findMatch_version2()
findMatch_old()
findMatch_working()
findMatch_for_real()
findMatch_20200229()
findMatch_thisoneisnewer()
findMatch_themostnewestone()
findMatch_thisisit()
findMatch_thisisit_for_real()
findMatch()
We can add automatic rules to find versioned methods with patterns.
Like many other patters we might create an internal policy and communicate.
Time and code evolution management is always present in software development. Luckily nowadays we have mature tools to address this problem.
That's why I write, because life never works except in retrospect. You can't control life, at least you can control your version.
Chuck Palahniuk
Searching for a concrete method implementation? Go back and forth, up and down.
TL;DR: Don't ab(use) hierarchies.
<?
abstract class Controller {
}
class BaseController extends Controller {
}
class SimpleController extends BaseController {
}
class ControllerBase extends SimpleController {
}
class LoggedController extends ControllerBase {
}
class RealController extends LoggedController {
}
<?
interface ControllerInterface {
}
abstract class Controller implements ControllerInterface {
}
final class LoggedControllerDecorator implements ControllerInterface {
}
final class RealController implements ControllerInterface {
}
Any linter can check for suspects against a max depth threshold.
Many novice programmers reuse code through hierarchies. This brings high coupled and low cohesive hierarchies.
Johnson and Foote established in their paper this was actually a good design recipe back in 1988. We have learned a lot from there.
We must refactor and flatten those classes.
An error arises from treating object variables (instance variables) as if they were data attributes and then creating your hierarchy based on shared attributes. Always create hierarchies based on shared behaviors, side.
David West
sort, doSort, basicSort, doBasicSort, primitiveSort, superBasicPrimitiveSort, who does the real work?
TL;DR: Shortcuts for mini wrappers shout for better solutions.
<?
final class Calculator {
private $cachedResults;
function computeSomething() {
if (isset($this->cachedResults)) {
return $this->cachedResults;
}
$this->cachedResults = $this->logAndComputeSomething();
}
private function logAndComputeSomething() {
$this->logProcessStart();
$result = $this->basicComputeSomething();
$this->logProcessEnd();
return $result;
}
private function basicComputeSomething() {
/// Do Real work here
}
}
<?
final class Calculator {
function computeSomething() {
// Do Real work here since I am Compute!
}
}
//Clean and cohesive class, single responsibility
final class CalculatorDecoratorCache {
private $cachedResults;
private $decorated;
function computeSomething() {
if (isset($this->cachedResults)) {
return $this->cachedResults;
}
$this->cachedResults = $this->decorated->computeSomething();
}
}
final class CalculatorDecoratorLogger {
private $decorated;
function computeSomething() {
$this->logProcessStart();
$result = $this->decorated->computeSomething();
$this->logProcessEnd();
return $result;
}
}
We can instruct our static linters to find wrapping methods if they follow conventions like doXXX(), basicXX() etc.
We came across this kind of methods some time in our developer life, We smelled something was not OK with them. Now is the time to change them!
The primary disadvantage of Wrap Method is that it can lead to poor names. In the previous example, we renamed the pay method dispatchPay() just because we needed a different name for code in the original method.
Michael Feathers
Classes are handy. We can call them and invoke them any time. Is this good?
TL;DR: Don't use your classes as a global point of access.
<?
final class StringUtilHelper {
static function reformatYYYYDDMMtoYYYYMMDD($date) {
}
}
class Singleton {
}
final class DatabaseAccessor extends Singleton {
}
<?
namespace Date;
final class DateFormatter {
function reformatYYYYDDMMtoYYYYMMDD(Date $date) {
}
//function is not static since class single responsibility is to create instances and not be a library of utils
}
namespace OracleDatabase;
class DatabaseAccessor {
//Database is not a singleton and it is namespace scoped
}
We can use almost any linter or create dependency rules searching for bad class references.
We should restrict our classes to small domains and expose just facades to the outside. This greatly reduces coupling.
Write shy code — modules that don't reveal anything unnecessary to other modules and that don't rely on other modules' implementations.
Dave Thomas
We are done for some time (not many).
But we are pretty sure we will come across even more smells very soon!
I keep getting more suggestions on twitter, so they won't be the last!
Send me your own!