요즘 전투적 몹프(=코드 이어달리기)를 하다보니
이전 타자가 코드를 깨끗하게 쓰지 않으면
뒷 타자가 내 코드를 리팩토링 하는데 시간을 쏟거나
코드를 잘못 이해해서 잘못된 코드를 작성하게 되는 걸 보면서
깨끗한 코드를 작성하는 것은 습관이여야하구나! 무릎을 탁 치는 깨달음을 얻었다
그래서 읽기 시작한 클린코드! 회사 출근 전에 30분씩 읽고 적용할거당
서론 + 1장.깨끗한 코드
- 프로그램을 짜다보면, 코드를 쓰는 시간보다 (남은 물론이고 자신도 정신없이 어지럽힌) 코드를 읽는 시간이 훨씬 더 많다는 사실
- 코드에 정직하고, 코드의 상태에 관하여 동료들에게 정직하고, 모엇보다도, 자기 코드에 대해서 자신에게 정직하라는 뜻이다. "처음 왔을 때보다 캠프장을 더 개끗이 치우고 떠나려고" 최선을 다했는가? 체크인하기 전에 코드를 깨끗하게 정리했는가? 이것은 부수적으로 고려할 사항이 아니다. 애자일이 추구하는 핵심적인 가치에 부합하는 사항이다.
- 르블랑의 법칙: 나중은 결코 오지 않는다.
- '코드 감각'이 있는 프로그래머는 나쁜 모듈을 보면 좋은 모듈로 개선할 방안을 떠올린다.
2장. 의미있는 이름
- 불용어를 추가한 이름 역시 아무런 정보도 제공하지 않는다. Product라는 클래스가 있다고 가정하자. ProductInfo 혹은 ProdcutData라 부른다면 (...) Info나 Data는 a, an, the 와 마참가지로 의미가 불분명한 불용어다.
ㄴ ㅋㅋㅋ 긁히네 평소 XXXInfo는 꽤나 자주 써왔는데 반성
- 예를 들어 도형을 생성하는 ABSTRACT FACTORY를 구현한다고 가정하자. 이 팩토리는 인터페이스 클래스다. 구현은 구체 클래스에서 한다. 그렇다면 두 클래스 이름은 어떻게 지어야 좋을까? IShapeFactory와 ShapeFactory? (...) 인터페이스 클래스 이름과 구현 클래스 이름 중 하나를 인코딩해야한다면 구현 클래스 이름을 택하겠다. ShapeFactoryImp나 심지어 CShapeFactory가 IShapeFactory보다 좋다.
- 클래스 이름과 객체 이름은 명사나 명사구가 적합하다. (...) Manager, Processor, Data, Info 등과 같은 단어는 피하고, 동사는 사용하지 않는다.
- 생성자를 중복정의할 때는 정적 팩토리 메서드를 사용한다. 메서드는 인수를 설명하는 이름을 사용한다.
- 우리는 문장이나 문단처럼 읽히는 코드 아니면 적어도 표나 자료 구조처럼 읽히는 코드를 짜는 데만 집중해야 마땅하다.
3장. 함수
- 함수를 만드는 첫째 규칙은 '작게'다! 함수를 만드는 둘째 규칙은 '더 작게!'다.
- 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다. (...) 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다. 어쨌거나 우리가 함수를 만드는 이유는 큰 개념을 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서가 아니던가
- 함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다. (...) 한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다. 특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어려운 탓이다.
- switch 문은 작게 만들기 어렵다. 또한 한 가지 작업만 하는 switch 문도 만들기 어렵다. 본질적으로 switch 문은 N가지를 처리한다. 하지만 각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법은 있다. 물론 다형성을 이용한다.
ㄴ 타입스크립트로 예를 들자면,
// don't
function calculatePay (employee: Employee) {
switch(employee.type) {
case 'PART_TIME':
return calculateParttimePay(employee);
case 'FULL_TIME':
return calculateFulltimePay(employee);
default:
throw new InvalidEmployeeType(employee.type)
}
}
위와 같은 함수는 좋지 못하다.
1. 만약 employee의 타입이 추가될 경우, calcultePay가 수정되어야한다.
2. calculatePay와 같은 형태의 deliverPay, isPayDay... 등등의 함수가 무한 양산된다.
`각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법은 있다. 물론 다형성을 이용한다.` 로 적용해보자
absctract class Employee {
absctract isPayday(): boolean
absctract calculatePay(): Money
absctract deliverPay(pay: Money): void
}
interface EmployeeFactory {
makeEmployee(type: EmployeeType): Employee;
}
class EmployeeFactoryImpl implements EmployeeFactory {
makeEmployee(type: EmployeeType): Employee {
switch(type) {
case 'PART_TIME':
return new ParttimeEmplyee()
case 'FULL_TIME':
return new FulltimeEmployee()
default:
throw new InvalidEmployeeType(type)
}
}
}
class PartimeEmployee extends Employee {...}
class FulltimeEmployee extends Employee {...}
- 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
- 함수에서 이상적 인수 개수는 0개다. 다음은 1개이고, 다음은 2개이다. 3항은 가능한 피하는 편이 좋다. 4개 이상은 특별한 이유가 있어도 사용하면 안 된다.
- 플래그 인수는 추하다. 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표하는 셈이니까! 플래그가 참이면 이걸 하고 거짓이면 저걸 한다는 말이니까!
- 부수 효과를 일으키지 마라! 부수 효과는 거짓말이다. 함수에서 한 가지를 하겠다고 약속하고선 남몰래 다른 짓도 하니까.
- 일반적으로 우리는 인수를 함수 입력으로 해석한다. 일반적으로 출력 인수는 피해야한다. 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택한다.
- 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다. 둘 다 하면 안 된다. 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다.
// don't
set(attribute: string, value: string): boolean {
// attribute를 찾아서 존재한다면 value로 설정하고
// 성공시 true, 실패시 false return
}
if (set("username", "bob")) {...}
// do
if (attributeExists("username") {
setAttribute("username", "bob")
}
- 오류 코드보다 예외를 사용해라! 오류 코드를 반환하면 호출자는 오류 코드를 곧바로 처리해야 한다.
4장. 주석
'생각 정리 > 독서' 카테고리의 다른 글
2024-03. 오브젝트 (1) (1) | 2024.02.08 |
---|---|
2024-02. 객체지향의 사실과 오해 (2) | 2024.02.02 |
2024-01. 소프트웨어 장인 (2) | 2024.01.17 |
부자 아빠, 가난한 아빠 (0) | 2022.09.06 |
위대한 나의 발견, 강점 혁명 (0) | 2022.07.04 |