한 줄 후기 : ㅋㅋ 진짜 광기다
무엇을 하는지가 아니라 무엇인지에 대해 이름을 지어라.
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 객체를 구현하면, 실질적으로 상당히 많은 부분을 작성하기 때문에, 내부적으로 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 에서는 객체와 메서드를 조합할 수 있다.
💡 바꿔 말하면, 객체를 객체답게 활용하지 않으면 별반 이점을 못 누린다는 말일 테다.
if, for, switch, while 과 같은 절차적인 statement (문장) 들은 코드에 포함되지 말아야 한다. 프로그래머는 데코레이터를 조합하는 일만 하고, 특정 시점에 app.run() 을 호출하면 데코레이터로 구성된 전체 피라미드가 반응하v도록 유도해야 한다.
val names = Sorted(
Unique(
Capitailized(
Replaced(
FileNames(
Directory("/var/users/*.xml"),
),
"([^.]+\\.xml",
"$1"
)
)
)
)
부모가 자식 객체에 접근하게 두지 마라. 자식이 부모의 것을 상속해 쓰게 하라.