CastleJo의 개발일지

Compose Method (179)

|

개요

어떤 메서드의 내부 로직이 한 눈에 이해하기 어렵다면,
그 로직을 의도가 잘 드러나며 동등한 수준의 작업을 하는 여러 단계로 나눈다.

void add(Object* element){
    if(!element->readOnly){
        int newSize = element->size + 1;
        if (newSize > element->length){
            Object[] newElements = new Object[elements.length + 10];
        }
        for (int i=0;i < element->size;i++){
            newElements[i] = elements[i];
        }
        element = newElements;
    }
    elements[size++] = element;
}

물론 못 읽을정돈 아니지만, 해석하기 귀찮다.

void add(Object* element){
    if (element->readOnly){
        return;
    }
    if (element->atCapacity()){
        element->grow();
    }
    addElement(element);
}

주석 없이도 한 눈에 읽어진다.

동기

그렇게 대단한 방법은 아니다. 오히려 엄청 사소한 방법이다.
그럼에도 이 리팩토링 방식은 최고의 리팩토링 방식 중 하나라고 불려진다.
작고 단순하여 몇초만에 이해할 수 있는 메소드 이다.
여러 메소드를 동등한 수준에서 나눈다.

이 리팩터링은 여러번의 Extract Method를 포함한다. 이 때 가장 어려운 것은 어떤 부분을 메소드 내부에 남기고, 어떤 부분을 다른 메소드로 뽑아낼 것 인지 결정하는 것이다.
너무 많은 코드를 뽑아내려 할 때 적절하게 작명하기 어려울것이다. 이런 경우 Inline Method를 적용하고 다시 분리할 방법을 찾아야 한다.

만약 메소드가 너무 많아져 성능이 떨어진다고 생각되는가?
여태 경험상 큰 문제는 없었고, 이는 BigO 방식으로 생각해봐도 큰 문제가 없다.

장점

  • 어떤 메소드가 무슨 일을 하고, 또 그 일을 어떻게 수행하는지 효과적으로 표현한다.
  • 동등한 수준의 작업을 하며 이름이 적절하게 붙은 몇 단계로 내부를 나눔으로써, 메소드를 단순하게 만든다.
  • 어떤 부분에서 문제가 생겼는지 쉽게 알 수 있어 디버깅이 쉬워진다.

단점

  • 작은 메소드가 지나치게 많이 생길 수 있다.

    이런 경우 Extract Class를 적용할 수 있다.

  • 로직이 여러 곳에 흩어지기 때문에 디버깅이 어려울 수 있다.

절차

이 방식은 매우 간단한듯 보이지만, 막상 해보면 느끼는것은 정답이 없다.
다만 다음과 같은 지침은 지시할 수 있다.

  • 작게 만든다

    Composed Method의 코드는 10줄을 잘 넘기지 않는다. 보통 5줄 정도이다.

  • 사용되지 않거나 중복된 코드를 제거한다
  • 코드의 의도가 잘 드러나도록 한다

    변수와 메서드, 파라미터 이름은 그 목적을 잘 표현하도록 짓는다

  • 단순화 한다

    가능한 한 단순하게 바꾼다. 보기좋게.

  • 동등한 수준으로 단계를 나눈다

    예를들어 세부 조건을 검사하는 로직과 몇개의 고수준 메소드를 호출하는 코드가 섞어있다면, 실패한것이다. 세부 조건 로직을 이름이 잘 지어진 별도의 메소드로 뽑아내 다른 고수준 메소드와 동등한 수준으로 맞춰야 한다.