Solid Principles: Single responsibility principle

The single responsibility principle is the first principle from the solid acronym.

“A class should have only one reason to change.”

Every module or class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class.

For example imagine the scenario of a navigation software.
We have a position which based on the direction given (north, south, west, east) the position should change.

The Position class contains values regarding the x and y axis position.

package com.gkatzioura.solid.single;

public class Position {

    private Integer xAxis;
    private Integer yAxis;

    public Position(Integer xAxis, Integer yAxis) {
        this.xAxis = xAxis;
        this.yAxis = yAxis;
    }

    public Integer getxAxis() {
        return xAxis;
    }

    public void setxAxis(Integer xAxis) {
        this.xAxis = xAxis;
    }

    public Integer getyAxis() {
        return yAxis;
    }

    public void setyAxis(Integer yAxis) {
        this.yAxis = yAxis;
    }
}

The direction is an enum representing the direction towards North, East, South and West.

package com.gkatzioura.solid.single;

public enum Direction {
    N,W,S,E
}

And at last there is a Navigator class which is responsible for navigating according to the direction and position change.


public class Navigator {

    public Position navigate(Position position, Direction direction) {
        ....
    }

}

In order to navigate properly the navigator should resolve the next position based on the direction. Also the navigator should fix the position in cases of values below 0.


public class Navigator {

    public Position navigate(Position position, Direction direction) {

        Position nextPosition = resolve(position,direction);
        Position fixedPosition =fix(nextPosition);
        return fixedPosition;
    }

    public Position resolve(Position position,Direction direction) {

        switch (direction) {
            case N:
                return new Position(position.getxAxis(),position.getyAxis()+1);
            case S:
                return new Position(position.getxAxis(),position.getyAxis()-1);
            case W:
                return new Position(position.getxAxis()-1,position.getyAxis());
            case E:
                return new Position(position.getxAxis()+1,position.getyAxis());
            default:
                throw new IllegalArgumentException();
        }
    }

    public Position fix(Position position) {

        return new Position(
                position.getxAxis()<0?0:position.getxAxis(),
                position.getyAxis()<0?0:position.getyAxis()
        );
    }

}

The problem with this approach is that in case the position validity criteria changes we have to change the Navigator class. The same applies in case of the position movement mechanisms changes. The navigator instead of just navigating is responsible for both resolving the next position and for fixing the new position.

An approach that does not break the single responsibility principle is to create a class that will resolve the next position and a class responsible for fixing the new position.

The NextPositionResolver class will resolve the next position based on the direction given.

package com.gkatzioura.solid.single;

public class NextPositionResolver {

    public Position resolve(Position position,Direction direction) {

        switch (direction) {
            case N:
                return new Position(position.getxAxis(),position.getyAxis()+1);
            case S:
                return new Position(position.getxAxis(),position.getyAxis()-1);
            case W:
                return new Position(position.getxAxis()-1,position.getyAxis());
            case E:
                return new Position(position.getxAxis()+1,position.getyAxis());
            default:
                throw new IllegalArgumentException();
        }
    }

}

The PositionRepairer class will fix the position in case of invalid x or y values.

package com.gkatzioura.solid.single;

public class PositionRepairer {

    public Position fix(Position position) {

        return new Position(
                position.getxAxis()<0?0:position.getxAxis(),
                position.getyAxis()<0?0:position.getyAxis()
        );
    }

}

The Navigator class will have as dependencies the NextPositionResolver and PositionRepairer classes in order to perform the navigation properly.

package com.gkatzioura.solid.single;

public class Navigator {

    private NextPositionResolver nextPositionResolver;
    private PositionRepairer positionRepairer;

    public Navigator(NextPositionResolver nextStepResolver,PositionRepairer positionRepairer) {
        this.nextPositionResolver = nextStepResolver;
        this.positionRepairer = positionRepairer;
    }

    public Position navigate(Position position, Direction direction) {

        Position nextPosition =  nextPositionResolver.resolve(position,direction);
        Position fixedPosition = positionRepairer.fix(nextPosition);
        return fixedPosition;
    }

}

You can find the source code on github. Next principle is the open/closed principle.

Also I have compiled a cheat sheet containing a summary of the solid principles.
Sign up in the link to receive it.

Advertisement

9 thoughts on “Solid Principles: Single responsibility principle

  1. Why dont you add PositionRepairer and PositionRepairer into Position class as a method. I thought that they are related with Position class and Position class must make its functionality.

    1. Yes it can be that way since the methods are very simple! What I was actually trying to communicate ,was the case of having a very complex routing or repairing algorithm which should be decoupled since it contained a pretty specific operation with various mechanisms thus being responsible only for this action.

  2. Why dont you add PositionRepairer and PositionRepairer into Position class as a method. I thought that they are related with Position class and Position class must make its functionality.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.