Upgrade to Pro — share decks privately, control downloads, hide ads and more …

読みやすいコードの書き方 第 6 回 / Code readability: Session ...

530

読みやすいコードの書き方 第 6 回 / Code readability: Session 6 (ver. 2, Ja)

第 6 回: 依存関係 I

---

セッションリスト

第 1 回: 導入と原則
- https://speakerdeck.com/munetoshi/code-readability-session-1-ver-2-ja

第 2 回: 命名
- https://speakerdeck.com/munetoshi/code-readability-session-2-ver-2-ja

第 3 回: コメント
- https://speakerdeck.com/munetoshi/code-readability-session-3-ver-2-ja

第 4 回: 状態
- https://speakerdeck.com/munetoshi/code-readability-session-4-ver-2-ja

第 5 回: 関数
- https://speakerdeck.com/munetoshi/code-readability-session-5-ver-2-ja

第 6 回: 依存関係 I
- https://speakerdeck.com/munetoshi/code-readability-session-6-ver-2-ja

第 7 回: 依存関係 II
- https://speakerdeck.com/munetoshi/code-readability-session-7-ver-2-ja

第 8 回: レビュー
- https://speakerdeck.com/munetoshi/code-readability-session-8-ver-2-ja

---

関連書籍 「読みやすいコードのガイドライン - 持続可能なソフトウェア開発のために」
- https://gihyo.jp/book/2022/978-4-297-13036-7

英語版のリスト: https://gist.github.com/munetoshi/65a1b563fb2c271f328c121a4ac63571#file-code-readability-links-md

以前のバージョン (英語): https://speakerdeck.com/munetoshi/code-readability

© 2019-2023 Munetoshi Ishikawa, supported by LINE corporation

Munetoshi Ishikawa

May 11, 2023
Tweet

Transcript

  1. ߨٛͷߏ੒ - ಋೖͱݪଇ - ࣗવݴޠ: ໋໊, ίϝϯτ - ܕͷߏ଄: ঢ়ଶ,

    ؔ਺ - ܕؒͷߏ଄: ґଘؔ܎I, ґଘؔ܎II - ϨϏϡʔ ґଘؔ܎ > ಋೖ
  2. ʮґଘؔ܎ʯͱ͸ 1/2 ྫ: Ϋϥε X ͕ Y ʹґଘ͍ͯ͠Δঢ়گ - X

    ͕ Y ͷΠϯελϯεΛϓϩύςΟͱͯ࣋ͭ͠ - X ͷϝιου͕ Y ΛҾ਺ɾ໭Γ஋ ͱͯ͠࢖͏ - X ͕ Y ͷϝιουΛݺͼग़͢ - X ͕ Y Λܧঝ͢Δ ͓͓Αͦ X தʹʮYʯ͕ग़ݱ 㱺 X ͸ Y ʹґଘ͍ͯ͠Δ ґଘؔ܎ > ಋೖ
  3. ґଘؔ܎ IɾII ͷ಺༰ ґଘؔ܎ I: - ݁߹౓ ґଘؔ܎ II: -

    ํ޲ - ॏෳ - ໌ࣔੑ ґଘؔ܎ > ಋೖ
  4. ґଘؔ܎ IɾII ͷ಺༰ ґଘؔ܎ I: - ݁߹౓ ґଘؔ܎ II: -

    ํ޲ - ॏෳ - ໌ࣔੑ ґଘؔ܎ > ݁߹౓
  5. ݁߹౓ 2/2 (Message coupling) (Data coupling) (Stamp coupling) (Control coupling)

    (Content coupling) (Common coupling) (External coupling) ґଘؔ܎ > ݁߹౓
  6. ࠓճͷ಺༰ (Message coupling) (Data coupling) (Stamp coupling) (Control coupling) (Common

    coupling) (Content coupling) (External coupling) ґଘؔ܎ > ݁߹౓ > ಺༰݁߹
  7. ෆਖ਼ͳ࢖͍ํͷڐ༰: Α͘ͳ͍ྫ class Caller { fun callCalculator() { calculator.parameter =

    42 calculator.calculate() val result = calculator.result ... ґଘؔ܎ > ݁߹౓ > ಺༰݁߹
  8. ෆਖ਼ͳ࢖͍ํͷڐ༰: Α͘ͳ͍ྫ class Caller { fun callCalculator() { calculator.parameter =

    42 calculator.prepare() calculator.calculate() calculator.tearDown() val result = calculator.result ... ґଘؔ܎ > ݁߹౓ > ಺༰݁߹
  9. ෆਖ਼ͳ࢖͍ํͷڐ༰: मਖ਼ྫ class Caller { fun callCalculator() { val result

    = calculator.calculate(42) ... class Calculator { fun calculate(type: Int): Int { prepare() ... tearDown() return ... ґଘؔ܎ > ݁߹౓ > ಺༰݁߹
  10. ಺෦ͷՄมͳঢ়ଶͷڞ༗: ʮΑ͘ͳ͍ʯྫ class UserListPresenter(val userList: List<UserData>) { fun refreshViews() {

    /* ... */ } class Caller { val userList: MutableList<UserData> = mutableListOf() val presenter = UserListPresenter(userList) ... fun addUser(newUser: UserData) { userList += newUser presenter.refreshViews() ґଘؔ܎ > ݁߹౓ > ಺༰݁߹
  11. ಺෦ͷՄมͳঢ়ଶͷڞ༗: Կ͕ʮΑ͘ͳ͍ʯ͔ UserListPresenter ͕֎෦ͷՄมͳঢ়ଶʹґଘ͍ͯ͠Δ - ଞͷ userList ͷॴ༗ऀʹΑͬͯҙਤͤͣঢ়ଶ͕ߋ৽͞ΕΔ - userList

    ͷมߋํ๏ͷ੍໿ʢ௥Ճɾ࡟আ౳ʣʹ੍໿͕ͳ͍ ෆਖ਼ͳঢ়ଶΛ࡞Γ΍͘͢ɺόάͷௐࠪ΋ࠔ೉ - UserListPresenter ͷࢀরͳ͠ʹߋ৽Մೳ ґଘؔ܎ > ݁߹౓ > ಺༰݁߹
  12. ಺෦ͷՄมͳঢ়ଶͷڞ༗: मਖ਼ྫ class UserListPresenter { val userList: MutableList<UserData> = mutableListOf()

    fun addUsers(newUsers: List<UserData>) { userList += newUsers // Update views with `userList` } (addUsers ͕ΞτϛοΫͳΒ) newUsers ͕ՄมͰ͋ͬͯ΋໰୊ͳ͍ ґଘؔ܎ > ݁߹౓ > ಺༰݁߹
  13. ࠓճͷ಺༰ (Message coupling) (Data coupling) (Stamp coupling) (Control coupling) (Common

    coupling) (External coupling) (Content coupling) ґଘؔ܎ > ݁߹౓ > ڞ௨݁߹ɾ֎෦݁߹
  14. άϩʔόϧม਺ͷར༻: ʮΑ͘ͳ͍ʯྫ var parameter: Int? = null var result: Int?

    = null class Calculator { fun calculate() { result = parameter + ... fun callCalculator() { parameter = 42 calculator.calculate() ґଘؔ܎ > ݁߹౓ > ڞ௨݁߹ɾ֎෦݁߹
  15. άϩʔόϧม਺ͷར༻: मਖ਼ྫ class Calculator { fun calculate(parameter: Int): Int =

    parameter + ... fun callCalculator() { val result = calculator.calculate(42) ґଘؔ܎ > ݁߹౓ > ڞ௨݁߹ɾ֎෦݁߹
  16. Մมͳγϯάϧτϯͷར༻: ʮΑ͘ͳ͍ʯྫ val USER_DATA_REPOSITORY = UserDataRepository() class UserListUseCase { suspend

    fun invoke(): List<User> = withContext(...) { val result = USER_DATA_REPOSITORY.query(...) ... ґଘؔ܎ > ݁߹౓ > ڞ௨݁߹ɾ֎෦݁߹
  17. Մมͳγϯάϧτϯͷར༻: मਖ਼ྫ class UserListUseCase( val userDataRepository: UserDataRepository ) { suspend

    fun invoke(): List<User> = withContext(...) { val result = userDataRepository.query(...) ... ґଘؔ܎ > ݁߹౓ > ڞ௨݁߹ɾ֎෦݁߹
  18. Ϋϥε಺ͷڞ௨݁߹ɾ֎෦݁߹: ʮΑ͘ͳ͍ʯྫ class Klass { var parameter: Int? = null

    fun firstFunction() { parameter = 42 secondFunction() } fun secondFunction() { /* Use `parameter` here... */ } ґଘؔ܎ > ݁߹౓ > ڞ௨݁߹ɾ֎෦݁߹
  19. Ϋϥε಺ͷڞ௨݁߹ɾ֎෦݁߹: मਖ਼ྫ class Klass { fun firstFunction() { val argument

    = 42 secondFunction(argument) } fun secondFunction(parameter: Int) { /* Use `parameter` here... */ } ґଘؔ܎ > ݁߹౓ > ڞ௨݁߹ɾ֎෦݁߹
  20. ࠓճͷ಺༰ (Message coupling) (Data coupling) (Stamp coupling) (Control coupling) (Content

    coupling) (Common coupling) (External coupling) ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  21. ෆඞཁʹཻ౓ͷେ͖͍৚݅෼ذ: ʮΑ͘ͳ͍ʯྫ fun updateView(isError: Boolean) { if (isError) { resultView.isVisible

    = true errorView.isVisible = false iconView.image = CROSS_MARK_IMAGE } else { resultView.isVisible = false errorView.isVisible = true iconView.image = CHECK_MARK_IMAGE } } ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  22. ෆඞཁʹཻ౓ͷେ͖͍৚݅෼ذ: मਖ਼ྫ fun updateView(isError: Boolean) { if (isError) { resultView.isVisible

    = true errorView.isVisible = false iconView.image = CROSS_MARK_IMAGE } else { resultView.isVisible = false errorView.isVisible = true iconView.image = CHECK_MARK_IMAGE } } ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  23. ෆඞཁʹཻ౓ͷେ͖͍৚݅෼ذ: मਖ਼ྫ fun updateView(isError: Boolean) { resultView.isVisible = !isError errorView.isVisible

    = isError iconView.image = getIconImage(isError) } fun getIconImage(isError: Boolean): Image = if (!isError) CHECK_MARK_IMAGE else CROSS_MARK_IMAGE ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  24. ಈ࡞ͷؔ࿈ੑ͕ബ͍৚݅෼ذ: ʮΑ͘ͳ͍ʯྫ 1/2 sealed class DataType { class UserName(...) :

    DataType() class BirthDate(...) : DataType() class ProfileImage(...) : DataType() } ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  25. ಈ࡞ͷؔ࿈ੑ͕ബ͍৚݅෼ذ: ʮΑ͘ͳ͍ʯྫ 2/2 fun updateUserView(dataType: DataType) = when(dataType) { is

    DataType.UserName -> { val userName = getUserName(dataType.userId) userNameView.text = userName } is DataType.Birthday -> { val birthDate = ... birthDayView.text = format(birthDate, ...) } is DataType.ProfileImage -> { ... } } ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  26. ಈ࡞ͷؔ࿈ੑ͕ബ͍৚݅෼ذ: Կ͕ʮΑ͘ͳ͍ʯ͔ - ͢΂ͯͷ෼ذΛಡ·ͳ͍ͱʮԿΛ͢Δ͔ʯ͕Θ͔Βͳ͍ - ݺͼग़͠ݩͷ৚݅ͱݺͼग़͠ઌͷಈ࡞ͷରԠ෇͚͕༰қͰͳ͍ updateUserView(DataType.ProfileImage(...)) fun updateUserView(dataType: DataType)

    = when(dataType) { is DataType.UserName -> { ... } // Need to check the condition... is DataType.Birthday -> { ... } is DataType.ProfileImage -> { ... } // Finally found ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  27. ৚݅෼ذͷ࡟আ: ద༻Ͱ͖Δঢ়گྫ ʮݺͼग़͠ݩ͕Ͳ͔͜ʯͰ৚͕ܾ݅·Δ class Caller1 { fun caller1() = presenter.updateUserView(DataType.UserName(...))

    } class Caller2 { fun caller2() = presenter.updateUserView(DataType.BirthDate(...)) } ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  28. ৚݅෼ذͷ࡟আ: վળྫ fun updateUserView(dataType: DataType) = when(dataType) { is DataType.UserName

    -> { val userName = getUserName(dataType.userId) userNameView.text = userName } is DataType.Birthday -> { val birthDate = ... birthDayView.text = format(birthDate, ...) } is DataType.ProfileImage -> { ... } } ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  29. ৚݅෼ذͷ࡟আ: վળྫ fun updateUserNameView(...) { val userName = getUserName(...) userNameView.text

    = userName } fun updateBirthdayView(...) { val birthDate = ... birthDayView.text = format(birthDate, ...) } fun updateProfileImage(...) { ... ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  30. ؇࿨͢΂੍͖ޚ݁߹ - ෆඞཁʹཻ౓ͷେ͖͍৚݅෼ذ - վળҊ: ૢ࡞ର৅ʹΑΔ෼ׂͷద༻ - ಈ࡞ͷؔ࿈ੑ͕ബ͍৚݅෼ذ - վળҊ

    1: ৚݅෼ذͦͷ΋ͷͷ࡟আ - վળҊ 2: ଞͷߏ଄Ͱͷஔ͖׵͑ ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  31. ଞͷߏ଄Ͱͷஔ͖׵͑: վળྫ enum class Binder(val viewId: ViewId) { fun updateView(holder:

    ViewHolder) = setContent(holder.getView(viewId)) abstract fun setContent(view: View) ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  32. ଞͷߏ଄Ͱͷஔ͖׵͑: վળྫ enum class Binder(val viewId: ViewId) { USER_NAME(USER_NAME_VIEW_ID) {

    override fun setContent(...) = ... } BIRTHDAY(BIRTHDAY_VIEW_ID) { override fun setContent(...) = ... ... fun updateView(holder: ViewHolder) = setContent(holder.getView(viewId)) abstract fun setContent(view: View) ґଘؔ܎ > ݁߹౓ > ੍ޚ݁߹
  33. ࠓճͷ಺༰ (Message coupling) (Data coupling) (Stamp coupling) (Control coupling) (Content

    coupling) (Common coupling) (External coupling) ґଘؔ܎ > ݁߹౓ > ελϯϓ݁߹
  34. ελϯϓ݁߹: ྫ updateUserView(userData) fun updateUserView(userData: UserData) { // Some property

    of `userData` are used userNameView.text = userData.fullName profileImage.setImageUrl(userData.profileImageUrl) // `userData.mailAddress` and `.phoneNumber` are not used } ґଘؔ܎ > ݁߹౓ > ελϯϓ݁߹
  35. ࠓճͷ಺༰ (Message coupling) (Data coupling) (Stamp coupling) (Control coupling) (Content

    coupling) (Common coupling) (External coupling) ґଘؔ܎ > ݁߹౓ > σʔλ݁߹
  36. σʔλ݁߹ جຊతͳܕͷΈΛҾ਺ɾ໭Γ஋Ͱड͚౉ؔ͢܎8 fun updateUserView(fullName: String, profileImageUrl: String) { userNameView.text =

    fullName profileImage.setImageUrl(profileImageUrl) } updateUserView(userData.fullName, userData.profileImageUrl) 8 ݫີʹ͸ɺString ͸จࣈͷ഑ྻ/Ϧετͱߟ͑ΒΕΔ ґଘؔ܎ > ݁߹౓ > σʔλ݁߹
  37. σʔλ݁߹: ஫ҙ఺ ελϯϓ݁߹ͷํ͕޷·͍͜͠ͱ΋͋Δ - Ҿ਺ͷҙຯ෇͚͕ඞཁͳ৔߹ - ܕݕࠪΛߦ͑Δ৔߹ fun updateUserView(fullName: String,

    profileImageUrl: String) = ... updateUserView(imageUrl, fullName) // !!! updateUserView(itemName, itemImageUrl) // !!! updateUserView(userA.fullName, userB.imageUrl) // !!! ґଘؔ܎ > ݁߹౓ > σʔλ݁߹
  38. ࠓճͷ಺༰ (Message coupling) (Data coupling) (Stamp coupling) (Control coupling) (Content

    coupling) (Common coupling) (External coupling) ґଘؔ܎ > ݁߹౓ > ϝοηʔδ݁߹
  39. ݁߹౓: ·ͱΊ ऑ͍݁߹Λ࢖͏ - ελϯϓ݁߹ɾσʔλ݁߹Λҙࣝͯ͠࢖͏ ஫ҙ͢Δ΂͖݁߹ - ಺༰݁߹: ಺෦ͷৄࡉͳಈ࡞ʹґଘ͢Δؔ܎ -

    ڞ௨݁߹ɾ֎෦݁߹: Մมͳάϩʔόϧσʔλ࢖͏ؔ܎ - ੍ޚ݁߹: ʮԿΛ͢Δ͔ʯΛҾ਺ͰܾΊΔؔ܎ ґଘؔ܎ > ݁߹౓ > ·ͱΊ