티스토리 뷰

객체지향 생활체조 원칙

 객체지향 생활체조 원칙은 소트웍스 엔솔러지 라는 책에서 다루고 있는 내용이다. 객체지향 프로그래밍을 잘 하기 위한 9가지 기본원칙을 제시하고 있다. 원칙의 제목을 처음 접하는 사람들은 생소하거나 다소 거부감이 들 수도 있을 것 같다. 그러나 이 책에서 주장하는 9가지 원칙에 숨은 의미를 곰곰히 생각해보고 소스에 적용하면, 어느새 깔끔한 구조의 객체지향 설계가 완성되는 것을 경험할 수 있다.

규칙 1. 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다

 들여쓰기의 depth를 2 이상으로 두지 말라는 지침이다. 예를 들어서 for 또는 while 반목문 안에 if문이 있으면 indent depth가 2인 코드가 된다. 반복문 안에 반복문이 존재하는 것도 마찬가지다.

숨은 의미

 이 원칙의 제시의도는 메서드 구조를 분리함으로써 한 단락은 하나의 일만 하도록 설계하자는 것이다. 로직이 잘개 쪼개질수록 메서드간의 로직적 결합도는 낮아지고, 응집도는 높아진다. 이러한 코드는 재사용성이 높고, 메서드명을 기반으로 작성되기 때문에 가독성도 좋다.

 프로그래밍은 글을 쓰는 행위와의 접점이 많다. 우리가 자기소개서와 같이 남에게 보여주기 위한 글을 쓸 때에는, 문장을 쪼개서 짧고 간결하게 만들려고 노력한다. 또 문단을 나누고 소제목을 붙이기도 하는데, 이러한 노력들이 객체지향 생활체조 원칙의 규칙 1과 비슷하다.

예시

 아래 코드는 숫자를 더하는 기능을 갖는 숫자 보드 클래스를 구현한 예시이다. 각각 규칙 1을 적용하기 전과 후로 표현해 보았다. 원칙을 적용하기 전 코드는 숫자를 더하는 메커니즘을 파악하기 위해 중첩된 for문을 해석해 주어야 한다. 반면에 원칙을 적용하고 난 후의 코드는 sumRows, sumColumns 라는 메서드명을 통해 행과 열의 합으로 더하기 메커니즘이 구성되어 있다는 것을 쉽게 표현한다.

    public class NumberBoard {

        int [][] boardNumbers = {
                {1, 2, 3, 4, 5},
                {6, 7, 8, 9, 10},
                {11, 12, 13, 14, 15}
        };

        public int sumBoardNumbers() {
            int sum = 0;
            for (int[] row : boardNumbers) {
                for (int column : row) {
                    sum += column;
                }
            }
            return sum;
        }
    }
    public class NumberBoard {

        int [][] boardNumbers = {
                {1, 2, 3, 4, 5},
                {6, 7, 8, 9, 10},
                {11, 12, 13, 14, 15}
        };

        public int sumBoardNumbers() {
            return sumRows();
        }

        private int sumRows() {
            int sum = 0;
            for (int[] row : boardNumbers) {
                sum += sumColumns(row);
            }
            return sum;
        }

        private int sumColumns(int[] row) {
            int sum = 0;
            for (int column : row) {
                sum += column;
            }
            return sum;
        }
    }

예외가 적용되는 경우

 컬렉션의 순회도중 early return을 해야 하는 경우가 있다. 보통 enum의 활용에서 발생하는 경우가 많다. 이렇게 컬렉션의 순회도중 루프를 먼저 끝내는 것이 효율적인 경우에는 반복문 내부의 조건문을 제한적으로 허용해도 된다고 생각한다.

public enum FinancialProduct {

    INSTALLMENT("1", "할부"),
    LEASE("2", "리스"),
    CREDIT("3", "신용")
    ;

    private String productCode;
    private String productName;

    FinancialProduct(String productCode, String productName) {
        this.productCode = productCode;
        this.productName = productName;
    }

    public static String nameOf(String productCode) {
        for (FinancialProduct value : values()) {
            if (value.productCode.equals(productCode)) {
                return value.productName;
            }
        }
        throw new IllegalArgumentException("상품이 존재하지 않습니다.");
    }
}

 Java 8부터 제공되는 stream api를 활용하면 indent depth를 높이지 않고도 위와 같은 코드를 처리할 수 있다.

public enum FinancialProduct {

    INSTALLMENT("1", "할부"),
    LEASE("2", "리스"),
    CREDIT("3", "신용")
    ;

    private String productCode;
    private String productName;

    FinancialProduct(String productCode, String productName) {
        this.productCode = productCode;
        this.productName = productName;
    }

    public static String nameOf(String productCode) {
        return Arrays.stream(values())
                .filter(product -> product.productCode.equals(productCode))
                .findFirst()
                .map(product -> product.productName)
                .orElseThrow(() -> new IllegalArgumentException("상품이 존재하지 않습니다."));
    }
}

 위 문장을 해체하면 결국 반복문 내부에 조건문이 들어간 셈이 아니냐 라고 반문할 수도 있을 것이다. 그렇기는 하다. 그런데 이 원칙을 지키려는 숨은 의도는 메서드의 역할을 분리하고 가독성을 높이는 것에 있다. indent의 depth를 낮추는 것은 가독성과 유지보수성 향상을 위한 것이지, 문법적으로 이를 제한하려는 것이 아니다.

참고자료 (소트웍스 엔솔러지)

https://book.naver.com/bookdb/book_detail.nhn?bid=5441199

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday