엘레강트 오브젝트 - 책 요약

Roeniss Moon·2025년 2월 23일
0

독서

목록 보기
36/36

한 줄 후기 : ㅋㅋ 진짜 광기다

-er 이름 사용 금지

무엇을 하는지가 아니라 무엇인지에 대해 이름을 지어라.

CashFormatter 는 actor 가 아니기 때문에 의인화할 수도 없고, 존중할만한 시민도 아니다.

class Cash (private val dollar: Int) { 
    fun usdString(){
        return "$ " + dollar
    }
}

객체는 attribute 가 아니라 capacity 로 특징지어져야 한다. 할 수 있는 일로 설명되어야 한다.

what he does 대신 what he is 로 설명하라

생성자에서 액션하지 마라

val app = App(Data(), Screen()) // app 을 생성하는 동안엔 아무것도 하지 않음
app.run() // 이제 시작

객체는 무언가 캡슐화해야 한다

진짜(pure) OOP 는 정적 메서드를 쓰지 않는다 (엄근진).

// Bad
class Year { 
    fun read(): Int { 
        return System.currentTimeMillis() / (1000*60*60*24*30*12) - 1970
    }
}

// Good
class Year(private val msec: Int)  { 
    fun read(): Int {
        return this.msec.read() / (1000*60*60*24*30*12) - 1970
    }
}

// Best
class Year(msec: Millis){
    private val num: Number

    init {
        this.num = Min( // minus
            Div(
                msec,
                Mul(1000,60,60,24,30,12)
            ),
            1970
        )
    }
    
    fun read(): Int { 
        return this.num.intValue()
    }
}

메서드 이름은 빌더 → 명사, 조정자 → 동사

빌더 : 뭔가를 만들고 새로운 객체를 반환

조정자 : 리턴 없음. 객체로 추상화된 실세계 엔티티를 수정

예를 들어 fun cookBrownie(): Brownie 는 적절한 메서드가 아니다. 그냥 procedure 다. 객체를 객체로 존중하지 않고, 일일이 명령한 셈이기 때문이다. cook brownie 는 내부적으로 “브라우니를” “요리해” 줄 것이고, 이러한 작동 방식이 함수 이름에 그대로 드러나 버린 상황이다.

같은 맥락에서, fun load(url): InputStream 은 틀리고 fun stream(url): InputStream 이 맞다. add 는 틀리고 sum 은 맞다.

💡 다른 책들에선, “how 대신 what 만 말하라”고 하는데, 이 책은 거기서 한 술 더 떠서 “what 을 말할 때 조금이라도 how 가 섞이지 않게 주의해라”라고 하는 것처럼 느껴진다

Mock 대신 Fake 를 써라

mock 은 “내부적으로 이걸 호출할 거라고 가정한다”는 얘기다. 전체 단위 테스트가 블랙박스 내부를 예상하는 것이다. 그 가정이 테스트의 중심이 되기 때문에 ‘리팩토링의 안전망’이라는 단위 테스트의 목적에 어긋난다.

💡 Fake 객체를 구현하면, 실질적으로 상당히 많은 부분을 작성하기 때문에, 내부적으로 fake 객체의 뭘 호출하든 상관안해! 라는 말처럼 되어서, 함수의 내부 상태 (블랙박스) 를 더 숨길 수 있다는 말 같다. 그 대가는 당연히 Fake 객체를 공들여 만들어야 한다는 점이리라.

정적 메서드 쓰지마라

(e.g., Math.max)

C 또는 어셈블리어의 서브루틴과 동일하다. 반면 우리가 OOP 의 관점을 최대한으로 활용하려면, 누가 누구인지 정의만 하고 객체들이 ‘필요할 때’ ‘스스로’ 상호작용하도록 ‘제어를 위임’해야 한다.

class Max(private val a: Number, private val b: Number) implements Number { 
}

Number x = Max(5, 9) // 최댓값을 '계산'하지 않고, 그저 x 는 '5 와 9 중 최댓값'이라고 정의

유틸리티 클래스과 싱글톤 쓰지마라

유틸리티 클래스 (아마 정적 메서드 뿐)과 달리, 싱글톤은 내부의 객체를 (fake 로) 교체할 수 있다는 특징(getInstance, setInstance)이 있다. 하지만 둘 다 쓰지 마라. 전역 변수와 다를 바 없다.

정적 메서드가 기술적으로 싱글톤이라는 눈속임을 가능케 했다.

FP 보다 OOP 가 낫다

FP 에선 함수만 사용할 수 있지만, OOP 에서는 객체와 메서드를 조합할 수 있다.

💡 바꿔 말하면, 객체를 객체답게 활용하지 않으면 별반 이점을 못 누린다는 말일 테다.

Composable Decorator 만 조작하여 코딩하라

if, for, switch, while 과 같은 절차적인 statement (문장) 들은 코드에 포함되지 말아야 한다. 프로그래머는 데코레이터를 조합하는 일만 하고, 특정 시점에 app.run() 을 호출하면 데코레이터로 구성된 전체 피라미드가 반응하v도록 유도해야 한다.

val names = Sorted(
	Unique(
		Capitailized(
			Replaced(
				FileNames(
					Directory("/var/users/*.xml"),
				),
				"([^.]+\\.xml",
				"$1"
			)
		)
	)
)

final or abstract (nothing else)

부모가 자식 객체에 접근하게 두지 마라. 자식이 부모의 것을 상속해 쓰게 하라.

profile
기능이 아니라 버그예요

0개의 댓글

관련 채용 정보