Skip to main content

[Five Lines Of Code] 2. 리팩터링 깊게 들여다보기

2.1 가독성 및 유지보수성 향상

어떤 시스템에는 한군데서 무언가를 수정하면 관련 없어 보이는 다른 곳에서 문제가 발생합니다. 추천 기능을 수정했는데 지불 시스템이 중단되는 온라인 쇼핑몰을 생각해보십시오. 그런 쉬스템을 취약하다(Fragile) 말합니다.

이 취약성의 근원은 일반적으로 전역상태입니다. 메서드로 생각하면 내부 변수가 전역 변수를 참조하는 것입니다. 전역상태의 문제는 데이터가 전역적일 경우 데이터가 연결된 다른 변수를 통해 누군가가 읽거나 변경할 수 있어 실수로 데이터가 손상될 수 있습니다. 코드에서 상태(조건)를 명시적으로 확인하지 않는(가정설정문으로만 확인하는) 속성을 불변속성이라고 합니다.

최대한 변수의 및 속성의 경우 가변성을 허용하면 안됩니다.

리팩터링의 세 가지 핵심은 다음과 같습니다.

  1. 의도를 전달함으로써 가독성 향상
  2. 불변속성의 범위제한을 통한 유지보수성 향상 (반지름은 항상 양수이다. Circle class 에서는 반지름에 대한 생성자 및 변경 코드에 이를 녹여 내야 합니다.)
  3. 범위 밖의 코드에 영향을 주지 않고 1항 2항을 수행

2.2 속도, 유연성 및 안정성 확보

2.2.1 상속보단는 컴포지션 사용

범위가 제한되지 않은 불변속성을 도입하는 일반적인 방법인 상속을 권장하지 않습니다.

상속보다는 컴포지션을 사용하라. 대부분의 리팩터링 패턴과 규칙은 구체적으로 객체 컴포지션을 돕기 위한 것들입니다. 즉, 객체가 내부에 다른 객체의 참조를 가지는 것 입니다.

interface Bird {
fun hasBeak(): Boolean
fun canFly(): Boolean
// fun canSwim(): Boolean (추가 된다면, 상속의 경우 오버라이딩 해야 한다는 것을 작업자가 기억해야 합니다.)
}

open class CommonBird : Bird {
override fun hasBeak(): Boolean = true
override fun canFly(): Boolean = false
}

// 상속
class Penguin : CommonBird() {
override fun canFly(): Boolean = true
}

// Composition
class Penguin : Bird {
private val bird: Bird = CommonBird()

override fun hasBeak(): Boolean = bird.hasBeak()
override fun canFly(): Boolean = bird.canFly()
}

canSwim이라는 새로운 메서드가 Bird에 추가한다면, 두 경우 모두 CommonBird에 메서드가 추가됩니다.

컴포지션 예에서 새로운 메서드 Bird 인터페이스를 사용하기에 canSwim 구현하지 않았기에 컴파일 오류가 발생합니다. 반면 상속의 경우 오버라이딩 해야 한다는 것을 작업자가 기억해야 합니다.

이와 더 불어 컴포지션의 가장 큰 장점은 추가로 변경이 가능하다는 것입니다. 이는 기존 기능에 영향을 주지 않고 기능을 추가하거나 변경할 수 있음을 의미합니다.

2.4 소프트웨어 분야에서 '도메인' 정의하기

소프트웨어는 실생활의 특정 측면을 모델링한 것입니다. 소프트웨어와 대응되는 실세계는 항상 존재합니다. 이 실제 세계의 구성요소를 소프트웨어의 도메인이라고 합니다.

소프트웨어를 개발할 때 도메인 전문가와 긴밀하게 협력해야 할 때가 많은데, 이는 그들의 용어와 문화를 배워야 한다는 것을 의미합니다. 프로그래밍 언어는 모호성을 호용하지 않기 때문에 때때로 전문가 조차도 낯선 새로운 엣지 케이스를 찾아야 합니다.