The Clean Code Talks - Don't Look For Things!

The Clean Code Talks - Don't Look For Things!

·

13 min read

png;base6470225ee43d5a4cf6.png

The source and idea for this post is via Google TechTalks

WHY? Clean Code is maintainable

Source code must be:

Readable && Extensible && Testable

We are going to stress the most neglected of these attributes.

To test a method:

1) we need to instantiate an object (system under test) and all the helpers(mock providers, stubs), etc. but it turns out that we put a lot of code in the constructor which makes it very difficult to create an object of it.

Suppose we have a pest call in our constructor. Now while testing, we actually have to make a pest call for the instantiation process.

class Story{
    public function __construct() {
        $pestObj = new Pest();
        $this->content = $pestObj->get('http://www.getStory.com');
    }
}

But instead of this, if I ask for what I need :

class Story{
    public function __construct($pestObj) {
        $this->content = $pestObj->get('http://www.getStory.com');
    }
}

What I can do now is, while testing, I can mock get method of $pestObj and we wont actually have to make get request.

Now everything looks alright but look at this for a moment:

1) Story knows about a pest client and makes a GET request just for its content.

How about this:

class Story{
    public function __construct($text) {
        $this->content = $text;
    }
}

and

class StoryStore{
    public function __construct($pestObj) {
        $this->pest = $pestObj;
    }
    public function getStory(){
        return new Story($pest->get('http://www.getStory.com'));
    }
}

This helps because tests are about instantiating small portions of the application and run them and assert some output. And if it is difficult to instantiate an object it's not just going to be difficult to test that object but also other parts that need that object. It's a kind of transitive problem.

1) So the test case has to successfully navigate the constructor each time the object is needed. So constructor ideally should be very simple. Just simple assignments and we are good to go.

Service Locator (context, service container):

In this system, we pass the responsibility of creating very hard to instantiate objects to the service locator, and then in code, we pass this service locator around.

class MyController extends Symfony\Bundle\FrameworkBundle\Controller\Controller {
    public function testAction() {
        $this->get('my_service')->func();
    }
}

and Symfony Controller does implement ContainerAwareInterface and has a service locator role.

But the problem with service locators is that they encourage the APIs that lie. You can't tell from looking at the definition of a class what its dependencies are because they aren't being injected, instead they are being yanked out of the service locator

Now we need to test a class that only says I need a service locator. But we have no idea what all we need to make the test run. we have to see the source code. what all are needed to run the class or see where it crashes and what needs to be overridden and most of all it violates the law of Demeter.

The Law of Demeter also known as principle of least knowledge is a coding principle, which says that a module should not know about the inner details of the objects it manipulates. If a code depends upon the internal details of a particular object, there is a good chance that it will break as soon as the internal of that object changes.

(Always ask for the things/references you need instead of searching for those references.)

According to Law of Demeter, a method M of object O should only call following types of methods :

  • Methods of Object O itself
  • Methods of Object passed as an argument
  • Method of an object, which is held in an instance variable
  • Any Object which is CREATED locally in method M

More importantly, the method should not invoke methods on objects that are returned by any subsequent method calls specified above and as Clean Code says "talk to friends, not to strangers"

-> Only Ask for objects you DIRECTLY need to operate on.

-> $a->getX()->getY() is a no no

-> $serviceLocator->getService() breaks law of demeter

-> Dependency Injection of specific objects we need.

Also if we want to reuse the class that requires a service locator then we have to supply the client with all the compile-time dependencies which include the service locator. But service locator contains many other services and dependencies. So here we have a problem, in order to give a class we need to give service locator and in order to give service locator we need to give most part of our application.

Breaking the Law of Demeter is Like Looking for a Needle in the Haystack

public class XMLUtils {
    public function getFirstBookCategoryFromXML(XMLMessage $xml) {
        return $xml->getXML()->getBooks()->getBookArrary(0)->getBookCategory();
    }
}

This code is now dependent upon lot of classes e.g.

  • XMLMessage
  • XML
  • Book
  • BookCategory

This means this function knows about XMLMessage, XML, Book, and BookCategory. It knows that XML has a list of books, which in-turn has BookCategory, that's a lot of information.

If any of the intermediate class or accessor methods in this chained method call changes, then this code will break.

It's an understanding between you and codes how to traverse the haystack and get the thing you need. All the things in between are irrelevant all u need is the book Category

Fundamentally this is what's wrong with the service locator, asking for things we don’t directly need but retrieving the needed from it.

In summary, abandon the use of a new operator. -> new operator ideally should either be inside the factory or in tests.

But in some cases, it is perfectly OK like instantiating a HashMap(), a collection. No need to ask for it in the constructor. This is because they are the leaves in your application graph. There is nothing behind these. It's the end of the road.

But for services, objects that need collaboration, those need to ask for their dependencies. Construction should be done either by DI container (Symfony container), or builders or factories.

Any kind of indirections we introduce in the code makes it very hard for writing tests. This cost becomes double the cost if this is done in the constructor. As of now it has to be shared by lots and lots of the tests that use that specific object.

Writing Testable Code

Flaw #1: Constructor does Real Work

  • "new" keyword in a constructor or at field declaration
  • Static method calls in a constructor or at field declaration
  • Anything more than field assignment in constructors
  • Object not fully initialized after the constructor finishes (watch out for initialize methods)
  • Control flow (conditional or looping logic) in a constructor
  • Code does complex object graph construction inside a constructor rather than using a factory or builder.
  • Adding or using an initialization block

Flaw #2: Digging into Collaborators

  • Objects are passed in but never used directly (only used to get access to other objects)
  • Law of Demeter violation: method call chain walks an object graph with more than one dot (.)
  • Suspicious names: context, environment, principal, container, or manager

Flaw #3: Brittle Global State & Singletons

  • Adding or using singletons
  • Adding or using static fields or static methods
  • Adding or using static initialization blocks
  • Adding or using registries
  • Adding or using service locators

Flaw #4: Class Does Too Much

  • Summing up what the class does includes the word “and”
  • The class would be challenging for new team members to read and quickly “get it”
  • The class has fields that are only used in some methods
  • The class has static methods that only operate on parameters

So now we know how to write UNTESTABLE code can wreak havoc on the person writing UTs:

Make Your Own Dependencies – Instantiate objects using new in the middle of methods, don’t pass the object in.

Heavy Duty Constructors – Make constructors that do lots of work in them.

Depend on Concrete Classes – Tie things down to concrete classes – to avoid interfaces wherever possible.

Conditional Slalom – Always, always, feel good when writing lengthy if branches and switch statements. These increase the number of possible execution paths that tests will need to cover when exercising the code under test.

Depend on Large Context Objects – Pass around ginormous context objects (or small ones with hard to construct contents).

Use Statics – Statics, statics everywhere!

Use More Statics – Statics are a really powerful tool to bring TDD Infected engineers to their knees. Static methods can’t be overridden in a subclass (sometimes subclassing a class and overriding methods is a technique for testing). When you use static methods, they can’t be mocked using mocking libraries.

Use Global Flags – Why call a method explicitly? Just like using a flag to set a flag in one part of your code, in order to cause an effect in a totally different part of your application.

Use Singletons Everywhere – Why pass in a dependency and make it obvious when you can use a singleton?

Look for Everything You Need – By Looking for things you are asserting your object's dominance as the object which knows where everything is. This will make the life of the tester hard since he will have to mimic the environment so that your code can get a hold of what it needs.

Utils, Utils, Utils! – Code smell? No way – code perfume! Litter about as many util and helper classes as you wish. These folks are helpful, and when you stick them off somewhere, someone else can use them too. That’s code reuse, and good for everyone, right? Be forewarned, the OO-police will say that functionality belongs in some object, as that object’s responsibility.

References:

  1. youtu.be/RlfLCWKxHJ0
  2. en.wikipedia.org/wiki/Law_of_Demeter
  3. slideshare.net/theojungeblut/2013-106-clean..
  4. nschoenmaker.nl/2013/11/defining-symfony-2-..
  5. misko.hevery.com/2008/07/24/how-to-write-3v..