Clean Code PHP – Classes

In my previous blog post I talked about Abstraction and Encapsulation. This are important concepts. Today we go deeper into classes. How and why create better and more clean classes.

Classes should be small

The most important rule about clean classes is that they should be small. The name should describe what responsibilities it should fulfill. If you use a general name then it often means it does to much. A class according to the Single Responsibility Principle (SRP) should have one and only one reason to change.

SRP is the easiest of the OO design principles but also the most abused principle. The reason is mostly because of limited time, and that we focus on getting our code work first. A lot of programmers think when a program works, the task is done. Thus failing to switch to the concern of clean and manageable code. You should refactor and make your classes smaller.

If you have a large number of big classes, it becomes harder to manage. Classes do more then one thing. And when you only need one part of it, you need to manage all the things you don’t need. If you create small classes each doing one and only on thing. Then you can easily reuse them trough your application.

This will also decrease the amount of brain power you need to use. The smaller classes are. The less you need to think about the code you don’t need. This allows you to focus more on what you are actually coding and increasing the quality of your code.

Cohesion

The best way to get small classes is to maintain cohesion. Classes should have a small amount of properties. Each method should use at least one or 2 of a classes properties. If you have a class where each property is used by each method, then you have maximum cohesion. This is not always possible, but we should aim for a high level of cohesion.

When you have a group of methods which only use a certain group of methods. Then we have low cohesion and this is a sign that those methods and properties should go in their own class.

If we break up large methods in smaller methods. Then we might have a lot of variables which we need to pass to each function. What you can do is create class properties so you don’t need to pass the same variables all the time. This unfortunately means we lose cohesion. Because now we have a group of methods which only uses a certain group of properties. But then this gives us the opportunity to create a new class for these properties to increase cohesion again. We have then successfully created more smaller classes with each of them only one responsibility and one reason to change.

Organize classes for change

In most applications there is a continuous change. New features get added, some get changed. But every change in code has a risk to break something. So the goal is to create classes and organize code so you lower the risk of breaking something.

For example, If we have a Filter class for our data table. We could create one Filter class and create for each kind of filter a public method. Now if we want to add an extra filter we need to open up the class and make changes. We then introduce risk to break something else.

<?php
class Filter {
    public function searchString($field, $searchString) 
    {
     /* */
    }

    public function searchSelect($field, $searchString) 
    {
        /* */
    }
}

Now if instead we created an abstract Filter class and extend that one with separate Filter classes. Each a different type of filter. We then reduce risk. We can easily add a new filter without needing to risk breaking another one. Not only we have implement SRP. We also support another OO design principle known as Open-Closed Principe (OCP). This means classes should be open for extension but closed for modification.

<?php
abstract Filter
{
    public function setField(string $field) 
    {
        /* */
    }
    
    public function search(string $searchString) 
    {
        /* */
    }
}

class StringFilter extends Filter
{
    public function search(string $searchString)
    {
        /* */
    }
}

class SelectFilter extends Filter
{
    public function search(string $searchString)
    {
        /* */
    }
}

We want to organize our code so when new features get added we need to update as little as possible of existing code. But instead we should be able to add new features by extending the application.

Isolating classes for change

You will have to change features, code will change. You have abstracted classes and implementations. If you create dependencies on implementation then you create a lot of challenges.

You should use interfaces. If you have a OrderShipping classes which depends on a UPS class to ship an order. We should not initiate that class in our OrderShipping class. We could pass the UPS class in the constructor of the OrderShipping class. But then we still depend on a concrete class. What we should to is to depend on an ShipperInterface. We can then pass the UPS class, but also any other shipper class. We can then for example also create a DummyShipper class to test our OrderShipping class. Or we can easily pass any other shipper and ship trough their API. This way you isolated yourself for changes. You used Dependency Inversion by using Dependency Injection.

Conclusion

The way to create clean classes is essentially applying the SOLID principles while creating classes. If you apply all this, you will be ready to face any change in your applications. You create more but leaner classes. You can focus more on details and limiting yourself from risks.  Spend more time developing your application instead of more time debugging and trying to understand it. You need to think to code to become most effective.