Chapter 3 ์žฌ์‚ฌ์šฉ์„ฑ

์žฌ์‚ฌ์šฉ์„ฑ์€ ๊ทธ๋งŒํผ์˜ ํž˜์ด ์žˆ๋Š” ๋งŒํผ ๊ต‰์žฅํžˆ ์œ„ํ—˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

A ์™€ B์—์„œ ๊ณตํ†ต ๋ถ€๋ถ„์„ ์ถ”์ถœํ•œ๋‹ค๋ฉด, ์ดํ›„์— ๊ณตํ†ต ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•  ์ผ์ด ์žˆ์„๋•Œ ํ•œ๊บผ๋ฒˆ์— ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ A๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ˆ˜์ •ํ•œ๊ฒƒ์ด B์—์„œ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ๋ฐ˜๋Œ€๋กœ B๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ˆ˜์ •ํ•œ ๊ฒƒ์ด A์—์„œ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

์žฌ์‚ฌ์šฉ์„ฑ์„ ๊ณ ๋ คํ•˜๋Š” ์ผ์€ ์ƒ๊ฐ๋ณด๋‹ค ์–ด๋ ต๊ณ , ๋‹ค์–‘ํ•œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

Item 19 knowledge ๋ฅผ ๋ฐ˜๋ณตํ•˜์—ฌ ์‚ฌ์šฉํ•˜์ง€ ๋ง๋ผ

ํ”„๋กœ์ ํŠธ์—์„œ ์ด๋ฏธ ์žˆ๋˜ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌ/๋ถ™์—ฌ๋„ฃ๊ธฐ ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ๋ฌด์–ธ๊ฐ€๊ฐ€ ์ž˜๋ชป๋œ ๊ฒƒ์ด๋‹ค.

knowledge

ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” knowledge๋Š” ๋„“์€ ์˜๋ฏธ๋กœ '์˜๋„์ ์ธ ์ •๋ณด'๋ฅผ ๋œปํ•จ

์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์—์„œ knowledge ๋ฅผ ๋ฝ‘๋Š”๋‹ค๋ฉด ๋‹ค์Œ ๋‘๊ฐ€์ง€๋กœ ๋‚˜๋‰ ์ˆ˜ ์žˆ์Œ

  • ๋กœ์ง(logic): ํ”„๋กœ๊ทธ๋žจ์ด ์–ด๋– ํ•œ ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ”„๋กœ๊ทธ๋žจ์ด ์–ด๋–ป๊ฒŒ ๋ณด์ด๋Š”์ง€
  • ๊ณตํ†ต ์•Œ๊ณ ๋ฆฌ์ฆ˜(common algorithm): ์›ํ•˜๋Š” ๋™์ž‘์„ ํ•˜๊ธฐ ์œ„ํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜

์œ„ ๋‘˜์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์€ ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋ณ€ํ™”์ด๋‹ค.

๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์€ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด์„œ ๊ณ„์† ๋ณ€ํ•˜์ง€๋งŒ, ๊ณตํ†ต ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ํ•œ๋ฒˆ ์ •์˜๋œ ์ดํ›„์—๋Š” ํฌ๊ฒŒ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋ชจ๋“  ๊ฒƒ์€ ๋ณ€ํ™”ํ•œ๋‹ค

ํ”„๋กœ๊ทธ๋žจ์—์„œ ์œ ์ผํ•˜๊ฒŒ ์œ ์ง€๋˜๋Š” ๊ฒƒ์€ '๋ณ€ํ™”ํ•œ๋‹ค๋Š” ์†์„ฑ' ์ด๋‹ค.

์˜ค๋Š˜๋‚ ์˜ ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ์ ํŠธ๋Š” ๋ช‡ ๋‹ฌ๋งˆ๋‹ค ์š”๊ตฌ ์‚ฌํ•ญ๊ณผ ๋‚ด๋ถ€์ ์ธ ๊ตฌ์กฐ๋ฅผ ๊ณ„์™ํ•ด์„œ ๋ณ€๊ฒฝํ•œ๋‹ค.

๋ชจ๋“ ๊ฒƒ์€ ๋ณ€ํ™”ํ•˜๊ณ  ์šฐ๋ฆฌ๋Š” ์ด์— ๋Œ€๋น„ํ•ด์•ผ ํ•œ๋‹ค. ๋ณ€ํ™”ํ•  ๋•Œ ๊ฐ€์žฅ ํฐ ์ ์€ knowledge๊ฐ€ ๋ฐ˜๋ณต๋˜์–ด ์žˆ๋Š” ๋ถ€๋ถ„์ด๋‹ค.

๋ฒ„ํŠผ ๋””์ž์ธ์˜ ๋ณ€๊ฒฝ ๊ฑด

ํ”„๋กœ์ ํŠธ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ๋ฒ”์šฉ์ ์ธ ๋ฒ„ํŠผ์ด ์žˆ๋‹ค. ๊ทธ๋ž˜ํ”ฝ ๋””์ž์ด๋„ˆ๊ฐ€ ์ด ๋ฒ„ํŠผ ๋ชจ์–‘์œผ๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๊ฒฐ์ •ํ•˜์˜€๋‹ค. ์ด ๋ฒ„ํŠผ๋ชจ์–‘์ด ์ ์šฉ๋œ ๋ชจ๋“  ๋ถ€๋ถ„์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค. ๋˜ํ•œ ์ ์šฉ ๊ณผ์ •์— ์‹ค์ˆ˜ํ•œ ๋ถ€๋ถ„์„ ์ฐพ๊ธฐ ์œ„ํ•ด ํ…Œ์Šคํ„ฐ์—๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์š”์ฒญํ•ด์•ผ ํ•œ๋‹ค.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ณ€๊ฒฝ ๊ฑด

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ํ…Œ์ด๋ธ” ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค ์ด ํ…Œ์ด๋ธ”์— ์˜์กดํ•˜๊ณ  ์žˆ๋Š” ๋ชจ๋“  SQL๊ตฌ๋ฌธ์„ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•˜๋‹ค ํ˜น์‹œ๋‚˜ ํ•œ ๋ถ€์„œ์—์„œ ์ด๋ฅผ ๋†“์น˜๊ณ  ์žˆ๋‹ค๋ฉด ๋งค์šฐ ์œ„ํ—˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

knowledge ๋ฐ˜๋ณต์ด ์–ผ๋งˆ๋‚˜ ์œ„ํ—˜ํ•˜๊ณ  ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค. knowledge ๋ฐ˜๋ณต์€ ํ™•์žฅ์„ฑ(scalable)์„ ๋ง‰๊ณ , ์‰ฝ๊ฒŒ ๊นจ์ง€๊ฒŒ(fragile) ๋งŒ๋“ ๋‹ค.

๊ฐœ๋ฐœ์ž๋Š” knowledge ๋ฐ˜ ๋ณต์„ ์ค„์ผ ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์™€ ๊ธฐ๋Šฅ๋“ค์„ ํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค.

ORM(Object Relational Mapping), DAO(Data Access Object) ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

์—ฌ๋Ÿฌ ์ถ”์ƒํ™”๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜๋งŽ์€ ์†”๋ฃจ์…˜์ด ์žˆ์œผ๋ฉฐ ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ฐ˜๋ณต์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

์–ธ์ œ ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณตํ•ด๋„ ๋ ๊นŒ?

knowledge ๋ฅผ ์ถ”์ถœํ•˜์—ฌ ๋ฐ˜๋ณต์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฒฝ์šฐ, ์˜ฌ๋ฐ”๋ฅด์ง€(์‹ ์ค‘ํ•˜์ง€) ๋ชปํ•œ ์ถ”์ถœ์€ ๋ณ€๊ฒฝ์„ ๋” ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค.

๋น„์ง€๋‹ˆ์Šค ๊ทœ์น™์ด ๋‹ค๋ฅธ ๊ณณ(source) ์—์„œ ์™”๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ๋‹ค๋ฅธ๊ณณ์—์„œ ์™”๋‹ค๋ฉด, ๋…๋ฆฝ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค.

๋‹จ์ผ ์ฑ…์ž„ ์›์น™ (SRP: Single Responsibility Principle)์„ ํ™œ์šฉํ•˜๋ฉด ์ž˜๋ชป๋œ ์ฝ”๋“œ ์ถ”์ถœ๋กœ ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹จ์ผ ์ฑ…์ž„ ์›์น™

๋‹จ์ผ ์ฑ…์ž„ ์›์น™์ด๋ž€ 'ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ด์œ ๋Š” ๋‹จ ํ•œ๊ฐ€์ง€์—ฌ์•ผ ํ•œ๋‹ค(A class should have only one reason to change)'

๋‹จ์ผ ์ฑ…์ž„์ด ์•Œ๋ ค์ฃผ๋Š” ๋‘๊ฐ€์ง€ ์‚ฌ์‹ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์„œ๋กœ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‚ฌ์šฉํ•˜๋Š” knowledge๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ๋ณ€๊ฒฝํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋งŽ๋‹ค.
    ๋น„์Šทํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋”๋ผ๋„, ์™„์ „ํžˆ ๋‹ค๋ฅธ knowledge๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • ๋‹ค๋ฅธ knowledge๋กœ ๋ถ„๋ฆฌํ•ด๋‘๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด, ์žฌ์‚ฌ์šฉํ•ด์„œ ์•ˆ๋˜๋Š” ๋ถ€๋ถ„์„ ์žฌ์‚ฌ์šฉํ•˜๋ ค๋Š” ์œ ํ˜น(์œ„ํ—˜)์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์ •๋ฆฌ

๋ชจ๋“ ๊ฒƒ์€ ๋ณ€ํ™”ํ•œ๋‹ค. ๊ณตํ†ต knowledge๊ฐ€ ์žˆ๋‹ค๋ฉด, ์ด๋ฅผ ์ถ”์ถœํ•ด์„œ ์ด๋Ÿฌํ•œ ๋ณ€ํ™”์— ๋Œ€๋น„ํ•ด์•ผ ํ•œ๋‹ค.

์—ฌ๋Ÿฌ ์š”์†Œ์— ๋น„์Šทํ•œ ๋ถ€๋ถ„์ด ์žˆ๋Š” ๊ฒฝ์šฐ, ๋ณ€๊ฒฝ์ด ํ•„์š”ํ• ๋•Œ ์‹ค์ˆ˜๊ฐ€ ๋ฐœํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์€ ์ถ”์ถœํ•˜๋Š”๊ฒƒ์ด ์ข‹๋‹ค.

์˜๋„ํ•˜์ง€ ์•Š๋Š” ์ˆ˜์ •์„ ํ”ผํ•˜๋ ค๋ฉด, ํ˜น์€ ๋‹ค๋ฅธ๊ณณ์—์„œ ์กฐ์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

Item 20 ์ผ๋ฐ˜์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ฐ˜๋ณตํ•ด์„œ ๊ตฌํ˜„ํ•˜์ง€ ๋ง๋ผ

์ด๋ฏธ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜น์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ™œ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์ ์ด ์žˆ๋‹ค.

  • ์ฝ”๋“œ ์ž‘์„ฑ์ด ๋นจ๋ผ์ง„๋‹ค.
    ํ˜ธ์ถœ์„ ํ•œ๋ฒˆ ํ•˜๋Š”๊ฒƒ์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋งŒ๋“œ๋Š” ์‹œ๊ฐ„ ๋Œ€๋น„ ๋น ๋ฅด๋‹ค.
  • ๊ตฌํ˜„์„ ๋”ฐ๋กœ ํ•˜์ง€ ์•Š์•„๋„, ํ•จ์ˆ˜์˜ ์ด๋ฆ„์„ ๋ณด๊ณ  ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜๊ฒŒ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
  • ์ง์ ‘ ๊ตฌํ˜„ํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์ž‡๋Š” ์‹ค์ˆ˜๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ๋‹ค.
  • ํ•œ๋ฒˆ์˜ ์ตœ์ ํ™”๋กœ ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ํ•จ์ˆ˜์—์„œ ๋™์ผํ•œ ์ตœ์ ํ™” ํ˜œํƒ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ดํŽด๋ณด๊ธฐ

override fun saveCallResult(item: SourceResponse) {
  var sourceList = ArrayList<SourceEntity>() 
  item.sources.forEach {
    var sourceEntity = SourceEntity()
    sourceEntity.id = it.id
    sourceEntity.category = it.category
    sourceEntity.country = it.country
    sourceEntity.description = it.description
    sourceEntity.add(sourceEntity)
  }
}

์œ„ ํ˜•ํƒœ๋ณด๋‹ค๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜, ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๊ทธ๋ž˜๋„ ์จ์•ผ ๊ฒ ๋‹ค๋ฉด .apply ๋กœ ํ™œ์šฉํ•ด์„œ ๋ชจ๋“  ๋‹จ์ผ ๊ฐ์ฒด๋“ค์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์•”๋ฌต์ ์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

override fun saveCallResult(item: SourceResponse) {
  val srouceEntries = item.sources.map(::sourceToEntry)
  db.insertSources(srouceEntries)
}

private fun sourceToEntry(source: Source) = SourceEntity()
  .apply {
    id = source.id
    category = source.category
    country = source.country
    description = source.description
  }

๋‚˜๋งŒ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ตฌํ˜„ํ•˜๊ธฐ

๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป๋Š” ํ•จ์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์ž˜๋ชป๋œ ์ผ์ด๋‹ค.

๋ชจ๋“  ํ•จ์ˆ˜๋Š” ํ…Œ์ŠคํŠธ๋˜์–ด์•ผ ํ•˜๊ณ , ๊ธฐ์–ต๋˜์–ด์•ผ ํ•˜๋ฉฐ, ์œ ์ง€๋ณด์ˆ˜๋˜์–ด์•ผ ํ•œ๋‹ค.

ํ•„์š”์—†๋Š” ํ•จ์ˆ˜๋ฅผ ์ค‘๋ณตํ•ด์„œ ๋งŒ๋“ค์ง€ ์•Š๊ฒŒ, ๊ธฐ์กด์— ๊ด€๋ จ๋œ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š”์ง€ ํƒ์ƒ‰ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค.

ํ™•์žฅํ•จ์ˆ˜ (Extension Function)

ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜๋ฉด์„œ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋“ค์„ ๋งˆ์น˜ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํด๋ž˜์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์›๋ž˜ ํ•จ์ˆ˜์ธ ๊ฒƒ ๋งˆ๋ƒฅ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์ˆ˜์ทจ์ธ ํƒ€์ž…(receiver type) : ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•  ํด๋ž˜์Šค
  • ์ˆ˜์ทจ์ธ ๊ฐ์ฒด(receiver object) : ํ™•์žฅ ํ•จ์ˆ˜ ๋‚ด๋ถ€๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ receiver type ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•œ ๊ฐ์ฒด
fun Iterable<Int>.product() = 
  fold(1) { acc, i -> acc * i }

ํ™•์žฅํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ๋งŒ๋“  ์ฝ”๋“œ๋Š” ์ •์  ๋ฐ”์ธ๋”ฉ์ด ๋œ๋‹ค.

ํ™•์žฅํ•จ์ˆ˜์˜ ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ํ•จ์ˆ˜๋Š” ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ํ–‰์œ„๋ฅผ ๋‚˜ํƒ€๋‚ด๊ธฐ ์ข‹๋‹ค.
    ํŠนํžˆ ๋ถ€์ˆ˜ ํšจ๊ณผ(side-effect) ์—†๋Š” ๊ฒฝ์šฐ ๋” ์ข‹๋‹ค.
  • ํ™•์žฅํ•จ์ˆ˜๋Š” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์ด ์ž‡๋Š” ๊ฐ์ฒด์—๋งŒ ์‚ฌ์šฉ์„ ์ œํ•œํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ข‹๋‹ค.
  • ์ˆ˜์ •ํ•  ๊ฐ์ฒด๋ฅผ ์•„๊ทœ๋จผํŠธ๋กœ ์ „๋‹ฌ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ํ™•์žฅ ๋ฆฌ์‹œ๋ฒ„๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋…์„ฑ ์ธก๋ฉด์—์„œ ์ข‹๋‹ค.
  • ํ™•์žฅ ํ•จ์ˆ˜๋Š” ๊ฐ์ฒด์— ์ •์˜ํ•œ ํ•จ์ˆ˜๋ณด๋‹ค ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์ž๋™ ์™„์„ฑ ๊ธฐ๋Šฅ๋“ฑ์œผ๋กœ ์ œ์•ˆ์ด ์ด๋ฃจ์–ด์ง€๋ฏ€๋กœ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

์ •๋ฆฌ

์ผ๋ฐ˜์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ฐ˜๋ณตํ•ด์„œ ๋งŒ๋“ค์ง€ ๋ง๋ผ.

ํŠน์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ฐ˜๋ณตํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ํ”„๋กœ์ ํŠธ ๋‚ด๋ถ€์— ์ •์˜ํ•˜๋ผ. ์ด๋Ÿฌํ•œ ์ฝ”๋“œ๋Š” ํ™•์žฅ ํ•จ์ˆ˜๋กœ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

Item 21 ์ผ๋ฐ˜์ ์ธ ํ”„๋กœํผํ‹ฐ ํŒจํ„ด์€ ํ”„๋กœํผํ‹ฐ ์œ„์ž„์œผ๋กœ ๋งŒ๋“ค์–ด๋ผ

์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ๊ณผ ๊ด€๋ จํ•ด์„œ ํ”„๋กœํผํ‹ฐ ์œ„์ž„๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

ํ”„๋กœํผํ‹ฐ ์œ„์ž„์„ ์‚ฌ์šฉํ•˜๋ฉด ์ผ๋ฐ˜์ ์ธ ํ”„๋กœํผํ‹ฐ์˜ ํ–‰์œ„๋ฅผ ์ถ”์ถœํ•ด์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ”„๋กœํผํ‹ฐ ์œ„์ž„์€ ํด๋ž˜์Šค ์œ„์ž„๊ณผ ๋น„์Šทํ•˜๊ฒŒ ํ”„๋กœํผํ‹ฐ ๊ฐ’์˜ get/set ์œ„์ž„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

by ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์œ„์ž„ํ•  ๊ฐ์ฒด๋ฅผ ํ”„๋กœํผํ‹ฐ ๋’ค์— ๋ช…์‹œํ•œ๋‹ค.

import kotlin.reflect.KProperty

class PropertyDelegate(var value: String) {
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    println("${property.name} get value ${this.value}")

    return value
  }

  operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
    println("${property.name} set value ${this.value} -> $newValue")

    this.value = newValue
  }
}

class Person {
  var name: String by PropertyDelegate("no name")
  val age: String by PropertyDelegate("31")
}

fun main() {
  val person = Person()

  println("person name is ${person.name}")  // person name is no name

  person.name = "jin"

  println("person name is ${person.name}")  // person name is jin

  println("person age is ${person.age}")    // person age is 31
}

์ฝ”ํ‹€๋ฆฐ์˜ stdlib๋Š” lazy ํ”„๋กœํผํ‹ฐ ํŒจํ„ด์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ lazy ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

fun getPostListFromServer(id: Int): List<Post> {
    println("get big data from server")
    return listOf(Post())
}

class Post

class Board(val id: Int) {
    val list: List<Post> by lazy { getPostListFromServer(this.id) }
}

fun main() {
    val board = Board(1)
    println("lazy init board list :: ${board.list}") // lazy init board list :: [Post@a09ee92]
    println("board list :: ${board.list}") // board list :: [Post@a09ee92]
}

// get big data from server
// lazy init board list :: [Post@2f4d3709]
// board list :: [Post@2f4d3709]

ํ”„๋กœํผํ‹ฐ ์œ„์ž„์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ด์™ธ์˜ ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ ์ด๋ฅผ ๊ฐ์ง€ํ•˜๋Š” observable ํŒจํ„ด์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

var items: List<Item> by 
  Delegates.observable(listOf()) { _, _, _ ->
    notifyDataSetChanged()
  }

val key: String? by 
  Delegates.observable(null) { _, old, new ->
    Log.e("key changed from $old to $new")
  }

ํ˜น์€ ์ผ์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ๋ชปํ•˜๋ฉด ๊ฐ’ ํ• ๋‹น์„ ์ œํ•œํ•˜๋Š” ๊ฑฐ๋ถ€๊ถŒ(vetoable)์„ ํ–‰์‚ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

import kotlin.properties.Delegates

class Person {
    // 200์‚ด ์ด์ƒ์€ ๊ฐ’ ์˜ค๋ฅ˜๋กœ ํŒ๋‹จ ๊ฐ’ ์…‹ํŒ…์„ ๊ฑฐ๋ถ€ํ•œ๋‹ค.
    var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
        newValue < 200
    }
}

fun main() {
    val person = Person()
    person.age = 31
    println("person age is ${person.age}")  // person age is 31

    person.age = 9999                       // ๊ฑฐ๋ถ€๋จ
    println("person age is ${person.age}")  // person age is 31
}

์ฝ”ํ‹€๋ฆฐ stdlib ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ”„๋กœํผํ‹ฐ ๋ธ๋ฆฌ๊ฒŒ์ดํ„ฐ๋ฅผ ์•Œ์•„๋‘๋ฉด ์ข‹๋‹ค.

  • lazy
  • Delegates.observable
  • Delegates.vetoable
  • Delegates.notNull

์ •๋ฆฌ

ํ”„๋กœํผํ‹ฐ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋Š” ํ”„๋กœํผํ‹ฐ์™€ ๊ด€๋ จ๋œ ๋‹ค์–‘ํ•œ ์กฐ์ž‘์„ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ปจํ…์ŠคํŠธ์™€ ๊ด€๋ จ๋œ ๋Œ€๋ถ€๋ถ„์˜ ใ…ˆ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ํŠน์ง•์„ ์ด์šฉํ•ด์„œ ๋‹ค์–‘ํ•œ ํ”„๋กœํผํ‹ฐ์˜ ๋™์ž‘์„ ์ถ”์ถœํ•ด์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์„ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜ ๋” ์ข‹์€ API ๋ฅผ ๋งŒ๋“ค ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ

https://jinn-blog.tistory.com/27

Item 22 ์ผ๋ฐ˜์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ตฌํ˜„ํ•  ๋•Œ ์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜๋ผ

์ œ๋„ค๋ฆญ ์ œํ•œ

ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์˜ ์„œ๋ธŒํƒ€์ž…๋งŒ ์‚ฌ์šฉํ•˜๊ฒŒ ํƒ€์ž…์„ ์ œํ•œํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

fun <T: Compareable<T>> Iterable<T>.sorted(): List<T> {
  /*...*/
}

fun <T, C: MutableCollection<in T>> Iterable<T>.toCollection(destination: C): C {
  /*...*/
}

class ListAdapter<T: ItemAdapter>(/*...*/) { /*...*/ }

ํƒ€์ž…์— ์ œํ•œ์ด ๊ฑธ๋ฆฌ๋ฏ€๋กœ, ๋‚ด๋ถ€์—์„œ ํ•ด๋‹น ํƒ€์ž…์ด ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ •๋ฆฌ

์ฝ”ํ‹€๋ฆฐ ์ž๋ฃŒํ˜• ์‹œ์Šคํ…œ์—์„œ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๊ต‰์žฅํžˆ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ์ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ type-safe ์ œ๋„ค๋ฆญ ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ์ œ๋„ค๋ฆญ ๊ฐ์ฒด๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

๋˜ํ•œ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๊ตฌ์ฒด ์ž๋ฃŒํ˜•(concrete type)์˜ ์„œ๋ธŒํƒ€์ž…์„ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

Item 23 ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์„€๋„์ž‰์„ ํ”ผํ•˜๋ผ

์•„๋ž˜ ์ฝ”๋“œ ์ฒ˜๋Ÿผ ํ”„๋กœํผํ‹ฐ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๊ฐ™์€ ์ด๋ฆ„์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.

class Forest(val name: String) {
  fun addTree(name: String) {
    // ...
  }
}

์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ์—์„œ ์™ธ๋ถ€์— ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ง€์—ญ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์˜ํ•ด ๊ฐ€๋ ค์ง€๊ฒŒ ๋˜๋Š”๋ฐ ์ด๋ฅผ ์„€๋„์ž‰(shadowing) ์ด๋ผ ๋ถ€๋ฅธ๋‹ค.

์ •๋ฆฌ

ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์„€๋„์ž‰์€ ์ดํ•ดํ•˜๊ธฐ๋„ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ํ”ผํ•ด์•ผ ํ•œ๋‹ค.

Item 24 ์ œ๋„ค๋ฆญ ํƒ€์ž…๊ณผ variance ํ•œ์ •์ž๋ฅผ ํ™œ์šฉํ•˜๋ผ.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค.

class Cup<T>

์—ฌ๊ธฐ์„œ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ T ๋Š” variance ํ•œ์ •์ž(in ํ˜น์€ out)๊ฐ€ ์—†์œผ๋ฏ€๋กœ invariant(๋ถˆ๊ณต๋ณ€) ํƒ€์ž…์ด๋‹ค.

invariant ํƒ€์ž…์€ ์ œ๋„ค๋ฆญ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ํƒ€์ž…๋“ค์ด ์„œ๋กœ ๊ด€๋ จ์ด ์—†๋‹ค.

๋งŒ์•ฝ ์–ด๋– ํ•œ ๊ด€๋ จ์„ฑ์„ ์›ํ•œ๋‹ค๋ฉด variance ํ•œ์ •์ž๋ฅผ ๋ถ™์ด๋ฉฐ out ์€ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ covariant(๊ณต๋ณ€์„ฑ) ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ ๋‹ค.

in ์€ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ contravariant(๋ฐ˜๊ณต๋ณ€)์œผ๋กœ ๋งŒ๋“ ๋‹ค.

PECS ๊ฐ€ ์ด ์„ฑ๊ฒฉ์„ ๋”ฐ๋ฅธ๋‹ค.

ํ•จ์ˆ˜ ํƒ€์ž…

ํŒŒ๋ผ๋ฏธํ„ฐ ์œ ํ˜•๊ณผ ๋ฆฌํ„ดํƒ€์ž…์— ๋”ฐ๋ผ ์„œ๋กœ ์–ด๋– ํ•œ ๊ด€๊ณ„๋ฅผ ๊ฐ–๋Š”๋‹ค.

์ฝ”ํ‹€๋ฆฐ์˜ ๊ณ„์ธต๊ตฌ์กฐ

์œ„ ๊ทธ๋ฆผ์—์„œ ๊ณ„์ธต ๊ตฌ์กฐ์˜ ์•„๋ž˜๋กœ ๊ฐ€๋ฉด, ํƒ€์ดํ•‘ ์‹œ์Šคํ…œ ๊ณ„์ธต์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์ด ๋” ๋†’์€ ํƒ€์ž…์œผ๋กœ ์ด๋™ํ•˜๊ณ , ๋ฆฌํ„ด ํƒ€์ž…์€ ๊ณ„์ธต ๊ตฌ์กฐ์˜ ๋” ๋‚ฎ์€ ํƒ€์ž…์œผ๋กœ ์ด๋™ํ•œ๋‹ค.

์ฝ”ํ‹€๋ฆฐ์˜ ํƒ€์ž…๊ณ„์ธต

์ฝ”ํ‹€๋ฆฐ์˜ ํ•จ์ˆ˜ ํƒ€์ž…์˜ ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์€ contravariant ์ด๋‹ค.

๋ชจ๋“  ๋ฆฌํ„ด ํƒ€์ž…์€ covariant ์ด๋‹ค.

์ฝ”ํ‹€๋ฆฐ์˜ in/out

variance ํ•œ์ •์ž์˜ ์•ˆ์ •์„ฑ

์ž๋ฐ”์˜ ๋ฐฐ์—ด์€ covariant ์ด๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ๋Š” ์ปดํŒŒ์ผ์€ ๋˜๋‚˜ ๋Ÿฐํƒ€์ž„๋•Œ ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค.

Integer[] numbers = {1, 4, 2, 1};
Object[] objects = numbers;
objects[2] = "B"            // Runtime Exception

์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ ๊ฒฐํ•จ์„ ํ•ด์†Œํ•˜๊ธฐ ์œ„ํ•ด Array(IntArray, CharArray ๋“ฑ)๋ฅผ invariant ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์„ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์–ด๋–ค ์„œ๋ธŒํƒ€์ž…์ด๋ผ๋„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ์•„๊ทœ๋จผํŠธ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ, ์•”๋ฌต์ ์œผ๋กœ ์—…์บ์ŠคํŒ… ํ•  ์ˆ˜ ์žˆ๋‹ค.

open class Dog
class Puppy: Dog()
class Hound: Dog()

fun takeDog(dog: Dog) {}

takeDog(Dog())
takeDog(Puppy())
takeDog(Hound())

์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํ™ฉ์€ ์•ˆ์ „ํ•˜์ง€ ์•Š๋‹ค.

์บ์ŠคํŒ… ํ›„ ์‹ค์งˆ์ ์ธ ๊ฐ์ฒด๊ฐ€ ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๊ณ , ํƒ€์ดํ•‘ ์‹œ์Šคํ…œ์—์„œ๋งŒ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

class Box<out T> {
  private var value: T? = null

  // ์ฝ”ํ‹€๋ฆฐ์—์„œ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ
  fun set(value: T) {
    this.value = value
  }

  fun get(): T = value ?: error("Value not set")
}

val puppyBox = Box<Puppy>()
val dogBox: Box<Dog> = puppyBox
dogBox.set(Hound())                 // Puppy ๋งŒ ๊ฐ€๋Šฅ

val dogHouse = Box<Dog>()
val box: Box<Any> = dogHouse
box.set("Some string")              // Dog ๋งŒ ๊ฐ€๋Šฅ
box.set(42)                         // Dog ๋งŒ ๊ฐ€๋Šฅ

variance ํ•œ์ •์ž ์œ„์น˜

varinace ํ•œ์ •์ž๋Š” ํฌ๊ฒŒ ๋‘ ์œ„์น˜์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฒซ๋ฒˆ์งธ๋Š” ์„ ์–ธ๋ถ€๋กœ ์ผ๋ฐ˜์ ์œผ๋กœ ์ด ์œ„์น˜์— ์‚ฌ์šฉ๋œ๋‹ค.

์ด๋Š” ํด๋ž˜์Šค์™€ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“ ๊ณณ์— ์˜ํ–ฅ์„ ์ค€๋‹ค.

class Box<out T>(val value: T)
val boxStr: Box<String> = Box("Str")
val boxAny: Box<Any> = boxStr

๋‘๋ฒˆ์งธ๋Š” ํด๋ž˜์Šค์™€ ์ดํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•˜๋Š” ์œ„์น˜์ด๋‹ค.

์ด ์œ„์น˜์— variance ํ•œ์ •์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ •ํ•œ ๋ณ€์ˆ˜์—๋งŒ variance ํ•œ์ •์ž๊ฐ€ ์ ์šฉ๋œ๋‹ค.

class Box<T>(val value: T)
val boxStr: Box<String> = Box("Str")
val boxAny: Box<out Any> = boxStr

์ •๋ฆฌ

  • ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ธฐ๋ณธ์ ์ธ ๋™์ž‘์€ invariant ํƒ€์ž…์ด๋‹ค.
  • in ํ•œ์ •์ž๋Š” ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ covariant ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.
  • out ํ•œ์ •์ž๋Š” ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ contravariant ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.
  • List์™€ Set์˜ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” covariant(out ํ•œ์ •์ž)์ด๊ณ , Array, MutableList, MutableSet, MutableMap ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” invariant(๋ฌด๊ณต๋ณ€) ์ด๋‹ค.
  • ํ•จ์ˆ˜ ํƒ€์ž…์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์€ contravariant(in ํ•œ์ •์ž) ํƒ€์ž…์ด๊ณ  ๋ฆฌํ„ด ํƒ€์ž…์€ covariant(out ํ•œ์ •์ž)ํƒ€์ž…์ด๋‹ค.

Item 25 ๊ณตํ†ต ๋ชจ๋“ˆ์„ ์ถ”์ถœํ•ด์„œ ์—ฌ๋Ÿฌ ํ”Œ๋žซํผ์—์„œ ์žฌ์‚ฌ์šฉํ•˜๋ผ

TL;DR - ๊ณตํ†ต ๋ชจ๋“ˆ์„ ์žฌ์‚ฌ์šฉํ•˜์ž