SOLID 원칙, 단일 책임의 원칙(Single Responsibility Principle)

현재 solid 원칙에 대해서 리프레쉬하기에 좋은 시기인 것 같습니다.  Solid의 S인 Single Responsibility Principle (단일 책임의 원칙) 부터 살펴보겠습니다.

 

단일책임의 원칙(Single Responsibility Principle)은 solid 원칙의 첫번째 원칙입니다.

 

하나의 클래스를 변경하는 이유는 반드시 하나여야만 한다

 

모든 모듈 혹은 클래스는 소프트웨어에 제공해야할 하나의 기능에 대한 책임을 가져야 합니다. 해당 책임은 클래스라는 단위로 캡슐화되어야 합니다.

 


 

단일책임의 원칙에 위배된 경우

 

예를 들어, 네비게이션 소프트웨어를 생각해보세요. 주어진 방향(동서남북)에 따라 위치가 변경되어야 합니다. 

 

Position 클래스는 xAxis yAxis 값을 가집니다.

package com.blackdog.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;
    }
}

 

Direction (방향) 클래스는 , , , 북의 의미를 담은 Enum으로 생성할 있습니다.

package com.blackdog.solid.single;

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

 

그리고 마침내 방향과 위치의 변화에 따라 방향을 찾는 역할을 가진 Navigator 네비게이터 클래스가 있습니다.

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()
        );
    }

}

 

위와 같은 클래스 설계는 Position 대한 유효성 기준이 변경된다 했을 , Navigator 클래스를 수정해야 합니다. 

 

Position 대한 이동 메커니즘(현재 +1 하고 있는데 해당 + 2 변경한다 던가 등에 대한 메커니즘) 변경되었을 경우에도 동일하게 Navigator 클래스를 수정해야 합니다.

 

Navigator 단순히 탐색하는 것이 아니라 다음 위치를 resolve 메소드를 이용하여 확인하고 새로운 포지션을 fix(수정) 메소드를 이용하여 고정하는 역할이 있습니다. (여기서부터 단일책임의 원칙을 위반하고 있습니다.)

 


 

그렇다면 단일책임의 원칙을 위반하지 않는 방법으로 개선해보겠습니다. 

 

Direction 에 따른 다음 Position을 resolve 하는 클래스를 생성하고, 새로운 Position에 대한 fix(수정) 하는 클래스를 생성하겠습니다.

 

NextPositionResolver 클래스는 Direction에 따라 다음 Position을 생성합니다. (resolve)

package com.blackdog.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();
        }
    }

}

 

PositionRepairer 클래스는 유효성에 맞지 않는 x, y 값에 대한 fix(수정) 의 기능을 가지고 있습니다.(물론 이에 대해서 fix는 구현할 소프트웨어에 따라 다를것입니다. 해당 예제에서는 음수의 경우 0값으로 고정 -> 예컨데 게임화면내에서 마우스가 모니터를 넘어서지 않는 현상을 동일하게 구현했다고 보면 됩니다.)

package com.blackdog.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()
        );
    }

 

Navigator 클래스는 탐색이라는 본인의 역할을 위해서 NextPositionResolverPositionRepairer 클래스에 대한 의존성을 가지게 됩니다.

package com.blackdog.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;
    }

}

 

 

이로써 Navigator 클래스의 탐색기능은 두개의 클래스의 기능으로 별도로 분리되어 분리된 클래스의 역할로써 위임되었고 위임된 역할에 의해서 탐색(navigate)기능을 구현할 수 있게 되었습니다.

댓글

Designed by JB FACTORY