Static: The S in STUPID

Presentation by @JeroenDeDauw

bit.ly/static-code

STUPID?

  • Static
  • Tight Coupling
  • Untestability
  • Premature Optimization
  • Indescriptive Naming
  • Duplication

SOLID

  • Single Responsibility Principle
  • Open/Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

What is wrong with this code?


public function doStuff() {
    // ...

    Logger::log( 'doing stuff during caturday is madness' );

    // ...

    DataStore::saveObject( $object );

    // ...
}
                        

public function doStuff() {
    Logger::log( 'doing stuff during caturday is madness' );
    DataStore::saveObject( $object );
}
                        
  • Method/class dependent on Logger and DataStore
  • What if the caller wants to use a different logger?

public function doStuff() {
    Logger::log( 'doing stuff during caturday is madness' );
    DataStore::saveObject( $object );
}
                        

public function testDoStuff() {
    // Some bootstrapping code ...

    $object->doStuff();
}
                        

Logger::log and DataStore::saveObject are ALWAYS called

DataStore::saveObject( $object );

class DataStore {
    public static function saveObject( $object ) {
        $loadBalancer = LoadBalancerFactory::getInstance();
        // ...
        $database->update( /* ... */ );
        // ...
        Logger::log( 'done saving object' );
    }
}
                        

Lets fix it!


class Logger {

    public static function log() {
        // ...
    }

    public static function resetForTesting() {
        // ...
    }

}
                    

Using an instance


class Logger {
    public function log() { /* ... */ }
}
                    

public function doStuff() {
    // ...
    Logger::getInstance()->log( 'doing stuff during caturday is madness' );
    // ...
}
                    

The SOLID solution


public function doStuff( Logger $logger, DataStore $dataStore ) {
    // ...

    $logger->log( 'doing stuff during caturday is madness' );

    // ...

    $dataStore->saveObject( $object );

    // ...
}
                        

Dependency Inversion Principle


public function __construct( Logger $logger, DataStore $dataStore ) {
    $this->logger = $logger;
    $this->dataStore = $dataStore;
}
                            

public function doStuff() {
    // ...

    $this->logger->log( 'doing stuff during caturday is madness' );

    // ...

    $this->dataStore->saveObject( $object );

    // ...
}
                        

public function testDoStuff() {
    $logger = new NullLogger();
    $dataStore = $this->getMock( 'DataStore' );

    $object = new SomeObject( $logger, $dataStore );

    $object->doStuff();

    // ...
}
                    
The secret in testing is writing testable code.

-- Miško Hevery

What about this static call?


class Awesome {

    public int calculateAwesomeness() {
        // ...

        // Clearly this can never be negative
        return Math.Abs( $awesomeness );
    }

}
                        

Static calls to leaves in the call graph are less evil

Leaves can stop being leaves


class Awesome {

    public static function calculateAwesomeness() {
        // ...

        $minAwesomeness = 9001;
        return min( $minAwesomeness, $awesomeness );
    }

}
                        

And now min awesomeness becomes config stored in your db...

Singleton

Often considered an anti-pattern

Should not be used all over

Global state is transitive

Global state

$GLOBALS['database']->saveStuff();
getDatabase()->saveStuff();
Database::saveStuff();
Database::singleton()->saveStuff();
ServiceRegistry::getService( 'database' )->saveStuff();
( new Database() )->saveStuff();

Conclusion

  • Static code is procedural, not OO
  • Static code causes tight coupling
  • Any state accessed via static code is global
  • Testing code with static calls is very hard
  • Static code suggests SRP violation

These slides

CC BY-SA 3.0, Jeroen De Dauw

Clone from GitHub or view at bit.ly/static-code


Attribution

Slide engine: reveal.js, Copyright (C) 2013 Hakim El Hattab

Instead of me