본문 바로가기
책/파이브 라인스 오브 코드

2장 - 리팩터링 깊게 들여다보기

by jeounpar 2024. 1. 29.

리팩터링?

-> 기능을 변경하지 않고 코드의 가독성유지보수가 쉽도록 코드를 변경하는 것

 

가독성 : 의도를 전달하기 위한 코드의 성질

코드에서 의도를 전달하는 방법

- 컨벤션, 주석, 변수, 메서드, 클래스 및 파일 이름, 공백 사용 등

 

유지보수성 : 버그를 고치거나 기능을 추가하기 위해 코드를 수정할 후보가 얼마나 많은지 나타냄

 

시스템이 취약하다 = 시스템에서 A를 수정했는데 B에도 영향이 가는 경우

이때, 취약성의 근원은 일반적으로 전역상태(global state) 때문!

전역상태(global state) : 프로그램이 실행되는 동안 변경될 수 있는 모든 것. ex) 모든 변수, DB의 데이터, 하드디스크 안의 파일 등

 

특히, 불변속성(invariant) 변수로 인해 시스템의 취약성이 증가한다

불변속성(invariant) : assertion 또는 주석으로만 표시한 속성. ex) "이 숫자는 절대 음수일 수 없습니다", "이 파일은 확실히 존재합니다" 등

시스템이 변경되거나 새로운 개발자가 팀에 추가될 때 불변속성이 유효한 상태로 유지되기는 어렵다

 

불변속성을 더욱 쉽게 볼 수 있도록 서로 가깝게 이동시켜 리팩터링을 진행하는 것이 좋음

'함께 변하는 것은 함께 있어야 한다' 라는 의미의 불변속성의 범위제한(localizing invariants)

 

 

상속보다는 컴포지션(composition)을 사용

컴포지션(composition) : 객체가 내부에서 다른 객체의 참조를 가지는 것
interface Bird {
	hasBeak(): boolean;
	canFly(): boolean;
}

class CommonBird implements Bird {
	hasBeak(): boolean {
		return true;
	}
	canFly(): boolean {
		return true;
	}
}

// 상속 사용
class Penguin extends CommonBird {
	canFly(): boolean {
		return false;
	}
}

interface Bird {
	hasBeak(): boolean;
	canFly(): boolean;
}

class CommonBird implements Bird {
	hasBeak(): boolean {
		return true;
	}
	canFly(): boolean {
		return true;
	}
}

class Penguin implements Bird {
	private bird = new CommonBird(); // 컴포지션
	hasBeak(): boolean {
		return this.bird.hasBeak();
	}
	canFly(): boolean {
		return false;
	}
}

 

위에 있는 코드는 상속을 사용했고 아래에 있는 코드는 컴포지션을 사용함

Bird 인터페이스에 canSwim 메서드를 추가하면 컴포지션을 사용한 Penguin 클래스는 canSwim을 구현하지 않았으므로 컴파일 에러가 발생함

즉, 프로그래머에게 Penguin이 수영을 할 수 있는지 여부의 구현을 강제함!

위의 예시에서 알 수 있듯이 컴포지션의 장점은 코드를 수정하는 것이 아니라 코드를 추가함으로써 코드에 변경을 준다는 것

**SOLID 원칙 중 Open-Close 원칙 - 확장에는 열려 있어야 하고 수정에 대해 닫혀있어야 함**