Refactoring

김기한·2022년 6월 7일
0
post-thumbnail

코드 리팩토링이란 프로그램의 내부 구조를 변경하는 것으로, 이미 구현된 기능의 변경이 없어야하고, 내부적인 코드의 성능 개선이 이루어져선 안된다. 리팩토링은 순전히 코드의 유지보수를 위해 행해진다.

Readability

일관된 스타일 (new line, space, indent)의 코드를 작성함으로써, 소스코드가 함수인지, 전역 변수 혹은 지역 변수인지 등을 쉽게 파악할 수 있고, 가독성을 높일 수 있다. 아래 두 코드를 비교해보자.

double[] doSomething(int x1, int x2, int x3) {double y[] = new
double[2]; double Q = x2*x2 – 4*x1*x3 ; if ( Q > 0 ) {
y[0] = ( – x2 + Math.sqrt(Q) ) / (2*x1) ; y[1] = ( – x2 –
Math.sqrt(Q) ) / (2*x1) ; } else
if ( Q == 0 ) y[0] = y[1] = (-x2) / (2*x1) ; return solution ;
}

적절한 위치에서 줄 바꿈이 일어나지 않고, 들여쓰기도 되어 있지 않다. 3번 라인이 어느 블럭에 위치하는지 조차 제대로 파악하기 힘든 코드이다. Readability가 매우 낮다.

double[] doSomething(int x1, int x2, int x3) {
	double y[] = new double[2] ;
	double Q = x2*x2 – 4*x1*x3 ;
	if ( Q > 0 ) {
		y[0] = ( – x2 + Math.sqrt(Q) ) / (2*x1) ;
		y[1] = ( – x2 – Math.sqrt(Q) ) / (2*x1) ;
	} else if ( Q == 0 )
		y[0] = y[1] = (-x2) / (2*x1) ;
	return y ;
}

적절한 위치에서 줄 바꿈이 일어났고, 블럭에 따라 들여쓰기가 이루어져있다. 5번과 6번 라인, 8번 라인이 각각 if와 else 블럭에 위치하는 것을 빠르게 파악가능한 코드로, Readability가 높다.

Understandability

코드 혹은 코드 조각이 어떤 행위를 하고, 어떤 의미를 갖는 코드인지 한눈에 파악하기 쉬운 정도를 나타낸다. Understandability가 높을수록 더 이해하기 쉬운 코드이다.

float compute(int k, int x) {
	float result = 0.0F;
	switch (k) {
		case 0:
			result = 2;
			if ( x> 2) result += (x- 2) * 1.5;
			break;
		case 1:
			result = 1.5;
			if (x> 3) result += (x- 3) * 1.5;
			break;
		case 2:
			result = x * 3;
			break;
		default: 
			break;
	}
	return result ;
};

변수들이 모두 k, x와 같은 의미를 알 수 없는 코드들로 네이밍이 되어있다. 이는 y라는 변수가 어떤 의미를 갖고 어떻게 사용되는지 예측하기가 힘들어지고, 전체적인 코드가 어떤 행위를 하게 되는지 파악하기 힘들다. Understandability가 낮은 코드이다.

enum MovieKind { REGULAR, CHILDREN, NEW_RELEASE } ;
float getCharge(const MovieKind kind, const Day daysRented) {
	float charge = 0.0F;
	switch (kind) {
		case REGULAR:
			charge = 2;
			if (daysRented > (Day)2) charge += (daysRented – (Day)2) * 1.5;
			break;
		case CHILDREN:
			charge = 1.5;
			if (daysRented > (Day)3) charge += (daysRented – (Day)3) * 1.5;
			break;
		case NEW_RELEASE:
			charge = (int)daysRented * 3;
			break;
		default: break;
	}
	return charge;
};

charge 라는 변수 명을 통해 요금을 나타내는 변수인 것을 확인할 수 있고, enum 타입 사용을 통해 kind의 값이 어떤 의미를 나타내는 지 한눈에 파악할 수 있다. Understandability가 높은 코드이다.

Refactoring: Rename

변수 및 함수의 이름을 변경함으로써 코드의 Understandability를 향상시킬 수 있다. 가장 간단하지만, 소스코드의 의미를 파악하기 위해 가장 기본적이고 중요한 리팩토링이다.

Refactoring: Extract Constant

흔히 매직 넘버라고 불리는 number 값을 상수로써 선언함으로써, 코드를 읽는 사람으로 하여금 해당 number의 의미를 잘 이해할 수 있도록 해준다.

private static final double GRAVITATIONAL_CONSTANT = 9.81;

public double getPotentialEnergy(final double mass, final double height) {
	return mass * GRAVITATIONAL_CONSTANT * height;
}

Refactoring: Extract Variable

계산이 길어지거나, 메서드의 호출 코드 길이가 길어져 코드의 의미를 파악하기가 힘들어질 때, 변수에 결과 값을 저장해두고 재사용하는 방식으로써 소스코드의 Understandability를 향상시켜준다.

final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;

if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
				// do something
}

Refactoring: Extract Method

메서드의 라인이 길어질수록, 해당 메서드가 어떤 역할을 수행하는 지 파악하기가 힘들어진다. 하나의 메서드에서 작업하는 내용들을 여러 메서드로 분리함으로써, 코드들에 의미를 부여해줄 수 있고, 이에 따라 코드의 Understandability를 향상시킬 수 있다.

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

if 문 내부에 존재하는 코드를 모두 분석해야, 비로소 해당 코드가 어떤 역할을 수행하는 지 이해할 수 있다.

public void add(Object element) {
	if ( readOnly ) return; // guard clause
	if ( needToGrow() ) grow();
	addElement(element);
}

하지만, 이렇게 needToGrow 라는 함수로 추상화하여 호출하면, 코드의 역할을 구체적으로 파악하기 전에 미리 해당 코드의 역할을 추측해볼 수 있다.

profile
vgihan's velog

0개의 댓글