본문 바로가기

Book Review/Clean Code

[클린 코드] 3장. 함수

1. SOLID 원칙

1) SRP 단일책임원칙 : 한 클래스는 하나의 책임만 가져야 한다. 변경에 의한 연쇄작용에서 자유로워지며 가독성 향상과 유지보수가 용이해진다. 실전에서는 쉽지 않지만 늘 상기해야 한다.

2) OCP 개방-폐쇄원칙 : 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있으나 변경에는 닫혀 있어야 한다. 변경을 위한 비용은 가능한 줄이고, 확장을 위한 비용은 가능한 극대화 해야 한다. 요구사항의 변경이나 추가사항이 발생하더라도, 기존 구성요소에는 수정이 일어나지 않고, 기존 구성 요소를 쉽게 확장해서 재사용한다. (추상화, 다형성)

3) LSP 리스코프 치환 원칙 : 서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다. (부모클래스에서 제공하는 기능을 서브클래스에서도 지원해줘야 한다. 음?) 서브 타입은 기반 타입이 약속한 규약(접근제한자, 예외 포함)을 지켜야 한다. 다형성과 확장성을 극대화하기 위해 상속보다는 인터페이스를 사용하는 것이 권장되고 있다. 합성(composite)을 이용할 수 도 있다. 

4) ISP 인터페이스 분리 원칙 : 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다. 가능한 최소한의 인터페이스만 구현한다. 만약 어떤 클래스를 이용하는 클라이언트가 여러 개고, 이들이 클래스의 특정 부분만 이용한다면, 여러 인터페이스로 분류하여 클라이언트가 필요한 기능만 전달한다. 

5) DIP 의존성 역전 원칙 : 상위 모델은 하위 모델에 의존하면 안된다. 둘 다 추상화에 의존해야 한다. 추상화는 세부 사항에 의존해서는 안된다. 세부 사항은 추상화에 따라 달라진다. 하위 모델의 변경이 상위 모듈의 변경을 요구하는 위계관계를 끊는다. 실제 사용관계는 그대로이지만, 추상화를 매개로 메세지를 주고 받으면서 관계를 느슨하게 만든다.

 

# DIP 예시

DIP - 새로운 카드 추가에 대한 확장에 유연하지 않고, 컨트롤러가 각 카드 서비스에 대한 의존성이 많아짐
DIP - 상위 모듈, 하위 모듈 모두 추상화에 의존할 수 있도록, PaymentService 라는 인터페이스를 구현한다.
DIP - 각 카드 타입에 맞게 서비스 팩토리로 구현하고, 서비스를 호출한다. PaymentController는 PaymentService에만 의존성을 갖는다.

 

# 간결한 함수 작성하기

길고, 여러 가지 기능을 포함한다.
함수 내 추상화 수준을 동일하게 맞춘다.

 

한 가지만 하기(SRP), 변경에 닫게 만들기(OCP)

계산도 하고, 머니도 생성? 새로운 타입이 추가된다면?

 

계산과 타입관리를 분리한다. 타입 처리는 팩토리에서 최대한 처리한다.

 

함수 인수

인수의 갯수는 0~2개가 적당하다. 3개 이상인 경우에는?

1) 객체를 인자로 넘기기

2) 가변 인자를 넘기기 (특별한 경우 (ex. 유틸)가 아니면 잘 쓰지 않는다.)

 

2. 안전한 함수 작성하기

안전한 함수란?  부수효과가 없는 함수이다.

값을 반환하는 함수가 외부 상태를 변경하는 경우, 부수 효과가 발생했다고 한다. 나의 기능만 수행하는 게 아니라 외부에 영향을 주기 때문에 위험한 함수이다. 

유효한 패스워드일 경우, 세션 초기화 진행하는 것은 범위를 벗어남. 아니면 메서드에 기능이 들어가야 함

 

3. 함수 리팩터링

1) 기능을 구현하는 서투른 함수를 작성한다. 

2) 테스트 코드를 작성한다. 함수 내부의 분기와 엣지 값마다 빠짐없이 테스트하는 코드를 짠다.

3) 리팩터링한다. 코드를 다듬고, 함수를 쪼개고, 이름을 바꾸고, 중복을 제거한다. 현재의 기능(아웃풋)은 동일하다는 가정하에 코드의 가독성을 높이는 것이므로 테스트코드를 먼저 작성한다.