[Java] 객체지향 설계 SOLID원칙과 SRP 단일 책임 원칙 필요이유와 적용 방법

    SOLID 원칙

    객체지향 설계 OOD(Object Oriented Design)의 정수라고 할 수 있는 5원칙이 집대성 됐는데 바로 이것이 SOLID 이다.

    SOLID는 아래 5가지 원칙의 앞 머리 알파벳을 따서 부르는 이름이다.

    • SRP(Single Responsibility Principle): 단일 책임 원칙
    • OCP(Open Closed Principle): 개방 폐쇄 원칙
    • LSP(Liskov Substitution Principle): 리스코프 치환 원칙
    • ISP(Interface Segregation Principle): 인터페이스 분리 원칙
    • DIP(Dependency Inversion Principle): 의존 역전 원칙

    위 원칙들은 응집도(High Cohesion)는 높이고, 결합도(Loose Coupling)는 낮추라는 원칙을 객체 지향 관점에서 재정립한 것이다.

    응집도가 높다는것은 모듈(클래스)하나의 책임에 집중해 독립성이 높은걸 말하고, 결합도가 낮다는것은 모듈간의 상호 의존성이 낮다는 뜻이다.

     

    ※ SOLID의 개념

    • SRP(단일 책임 원칙): 어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다.
    • OCP(개방 폐쇄 원칙): 자신의 확장에는 열려 있고, 주변의 변화에 대해서는 닫혀 있어야 한다.
    • LSP(리스코프 치환 원칙): 서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다.
    • ISP(인터페이스 분리 원칙): 클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다.
    • DIP(의존 역전 원칙): 자신보다 변하기 쉬운 것에 의존하지 마라

     

    SRP - 단일 책임 원칙

    어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이여야 한다. by 로버트 C. 마틴

    단일 책임 원칙은 하나의 모듈은 한 가지 책임을 가져야 한다는 의미이고, 하나의 모듈이 바뀌는 이유는 한 가지여야 한다고 설명 되기도 한다. 단일책임 원칙을 잘 지키고 있다면, 어떤 변경이 필요할 때 수정 대상이 명확해 진다.

     

    단일 책임 원칙을 지키지 않았을 때

    남자는 군대를 가고 여자는 군대를 안간다는 가정아래, 아래 코드를 보면 사람 클래스안에 성별과 군번이 있다. 

    사람example 코드 안에서 로미오와 줄리엣 객체를 만들고 로미오가 남자라서 군번을 넣었다. 그리고 객체들을 사람 배열에 넣어서 print군번() 메소드를 실행 시켰다. 

    그러면 로미오는 "나의 군번은 102121 입니다" 가 나오지만 줄리엣은 "나의 군번은 null 입니다" 가 출력 될 것이다. 

    public class 사람 {
        String 성별;
        String 군번;
    
        public 사람(String 성별) {
            this.성별 = 성별;
        }
    
        public void print군번() {
            System.out.println("나의 군번은 " + 군번 + " 입니다");
        }
    }
    
    public class 사람example {
        public static void main(String[] args) {
            사람 로미오 = new 사람("남자");
            사람 줄리엣 = new 사람("여자");
    
            로미오.군번 = "102121";
    
            사람[] 사람arr = new 사람[]{로미오, 줄리엣};
    
            for (사람 사람 : 사람arr) {
                사람.print군번();
            }
        }
    }

     

    위 문제를 해결하려면 print군번() 메소드에서 성별이 남자이면 출력하고 성별이 여자면 출력안하게 아래처럼 if문을 써야 한다. 

    public void print군번() {
        if(성별.equals("남자")) {
            System.out.println("나의 군번은 " + 군번 + " 입니다");
        } else {
        	System.out.println("군대에 가지 않았습니다.");
        }
    }

     

    위 처럼 단일 책임 원칙을 지키지 않았을 때 if문이 붙게 되고 로직이 다양할 경우에는 if문이 덕지덕지 붙어 결국에는 디버깅도 힘들고 기능 수정도 힘들것이다. 단일 책임 원칙을 지키지 않았을 때 대표적인 냄새가 분기 처리를 위한 if문이다. 위 소스에서 여자도 군대에 갈 수 있어서 로직 수정이 필요하다면 if문 안에서 수정을 해야 한다. 소스가 복잡하면 복잡할 수록 필요한 부분만 수정하기 힘들어지고 수정을 하더라도 생가지도 못한 다른 곳에서 에러가 발생 할 수 있다.

     

    단일 책임 원칙을 지켰을 때

    위 문제를 해결하려고 if문을 없애 단일 책임 원칙을 적용하여 리팩터링 하였다.

    public abstract class 사람 {
        String 성별;
        String 군번;
    
        abstract public void print군번();
    }
    
    public class 남자 extends 사람 {
        public 남자(String 성별) {
            this.성별 = 성별;
        }
    
        @Override
        public void print군번() {
            System.out.println("나의 군번은 " + 군번 + " 입니다");
        }
    }
    
    public class 여자 extends 사람 {
        public 여자(String 성별) {
            this.성별 = 성별;
        }
    
        @Override
        public void print군번() {
            System.out.println("군대에 가지 않았습니다.");
        }
    }
    
    public class 사람example {
        public static void main(String[] args) {
            사람 로미오 = new 남자("남자");
            사람 줄리엣 = new 여자("여자");
    
            로미오.군번 = "102121";
    
            사람[] 사람arr = new 사람[]{로미오, 줄리엣};
    
            for (사람 사람 : 사람arr) {
                사람.print군번();
            }
        }
    }

     

    사람을 abstract 클래스로 만들고 print군번() 을 abstract로 만들어 재정의 하게 만들었다. 그리고 사람 클래스를 남자, 여자 클래스로 따로 만들어서 상속 시켰다. 남자 클래스와, 여자 클래스에서 print군번() 을 따로 만들어서 if문으로 분기 처리 하지않고 다르게 처리 하였다.

    단일 책임 원칙을 고려한 것이다. 

    만약 여자도 군대에 갈 수 있다 라고 수정을 하게 되었을때 여자 클래스에서 print군번() 메소드만 수정해 주면 된다. 

     

    비고

    객체 지향 4대 특성중 가장 관계가 깊은 것은 모델링 과정을 담당하는 추상화 이다. 애플리케이션 경계를 정하고 추상화를 통해 클래스들을 선별하고 속성과 메서드를 설계할 때 반드시 단일 책임 원칙을 고려하는 습관을 가져야 한다.

     

    테이블을 설계할 때도 단일 책임 원칙을 고려해야 한다. 테이블 설계할때 정규화 과정을 거치는데 이 과정이 테이블과 필드에 대한 단일 책임 원칙의 적용이라고 할 수 있다.

     

     

    참고 : 스프링 입문을 위한 자바 객체 지향의 원리와 이해 / 김종민 / 위키북스

    댓글

    Designed by JB FACTORY