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

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

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

第 3 回: コメント

---

セッションリスト

第 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

More Decks by Munetoshi Ishikawa

Other Decks in Programming

Transcript

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

    ؔ਺ - ܕؒͷߏ଄: ґଘؔ܎I, ґଘؔ܎II - ϨϏϡʔ ίϝϯτ > ಋೖ
  2. ίϝϯτ ίʔυதʹॻ͔ΕΔ஫ऍɾઆ໌ - ௨ৗɺίϯύΠϥ΍ΠϯλϓϦλ͔Β͸ແࢹ͞ΕΔ /** * Explanation of `fooFunction`... */

    fun fooFunction(parameter: Parameter /* note of the parameter */) { barFunction() // Explanation of why `barFunction` is called /* A comment with multiple lines. */ ίϝϯτ > ಋೖ
  3. ίϝϯτʹΑΔϦϑΝΫλϦϯά: ྫ /** * Adds a new pair of keyword

    and the definition to this dictionary, * can be referenced by [getDefinition(String)]. * * If the keyword is already registered, this registration fails. * Then, this returns a boolean representing whether the registration * succeeded. */ fun add(newData: Pair<String, String>): Boolean ಈ࡞ͷ୯७͞ͷׂʹ௕͍ίϝϯτ͕ඞཁ ίϝϯτ > ಋೖ
  4. ίϝϯτʹΑΔϦϑΝΫλϦϯά: ݁Ռ /** * Adds or overwrites a definition of

    a given [keyword]. * The registered definition is obtained by [getDefinition(String)]. */ fun registerDefinition(keyword: String, definitionText: String) ಈ࡞ͷ୯७͞ʹݟ߹ͬͨίϝϯτΛॻ͘ ίϝϯτ > ಋೖ
  5. ୅දతͳίϝϯτͷछྨ - υΩϡϝϯςʔγϣϯ: Ϋϥεɾؔ਺౳ʹॻ͘ܗࣜతͳίϝϯτ /** * A list of pairs

    of a string keyword and the explanation text. * * ... (details of usage, limitations, etc.)... */ class Dictionary { ... } ίϝϯτ > ಋೖ
  6. ୅දతͳίϝϯτͷछྨ - υΩϡϝϯςʔγϣϯ: Ϋϥεɾؔ਺౳ʹॻ͘ܗࣜతͳίϝϯτ - ඇܗࣜతͳίϝϯτ: ίʔυதͷ༷ʑͳ৔ॴʹॻ͔ΕΔίϝϯτ - To do

    ίϝϯτ: // TODO: , // FIXME: - IDEɾίϯύΠϥɾίʔυੜ੒༻ͷίϝϯτ: // $COVERAGE-IGNORE$ ίϝϯτ > ಋೖ
  7. ୅දతͳίϝϯτͷछྨ - υΩϡϝϯςʔγϣϯ: Ϋϥεɾؔ਺౳ʹॻ͘ܗࣜతͳίϝϯτ - ඇܗࣜతͳίϝϯτ: ίʔυதͷ༷ʑͳ৔ॴʹॻ͔ΕΔίϝϯτ - To do

    ίϝϯτ: // TODO: , // FIXME: - IDEɾίϯύΠϥɾίʔυੜ੒༻ͷίϝϯτ: // $COVERAGE-IGNORE$ ίϝϯτ > ಋೖ
  8. υΩϡϝϯςʔγϣϯͷର৅ - Ϋϥε, ΠϯλʔϑΣΠε, ྻڍܕ, ߏ଄ମ, ϓϩτίϧ, τϨΠτ... - ม਺,

    ϓϩύςΟ, ϑΟʔϧυ, ύϥϝʔλ, ఆ਺... - ؔ਺, ϝιου, खଓ͖, αϒϧʔνϯ... - είʔϓ, ύοέʔδ, Ϟδϡʔϧ, ໊લۭؒ... ίϝϯτ > υΩϡϝϯςʔγϣϯ
  9. υΩϡϝϯςʔγϣϯͷϑΥʔϚοτ ίϝϯτͷ։࢝෦෼ - `/**`: Kotlin, Java, Swift and Objective-C -

    `///`: Swift and Objective-C - `/*!`: Objective-C ֤ υΩϡϝϯςʔγϣϯδΣωϨʔλͷϑΥʔϚοτ Λࢀর ྫ: KDoc, Javadoc, Swift Markup, HeaderDoc, Doxygen ίϝϯτ > υΩϡϝϯςʔγϣϯ
  10. Ξϯνύλʔϯ: ࣗಈੜ੒͞ΕͨίϝϯτΛ์ஔ /** * @param keyword * @return */ fun

    getDescription(keyword: String): String ίϝϯτ > υΩϡϝϯςʔγϣϯ > Ξϯνύλʔϯ
  11. Ξϯνύλʔϯ: ఆ໊ٛͱಉ͡ /** * Gets the description for a keyword.

    */ fun getDescription(keyword: String): String ίϝϯτ > υΩϡϝϯςʔγϣϯ > Ξϯνύλʔϯ
  12. Ξϯνύλʔϯ: ίʔυΛ຋༁͚ͨͩ͠ /** * Calls [doA] if `conditionA` is satisfied.

    * Otherwise, calls [doB] and if ... */ fun getDescription(keyword: String): String { if (conditionA) { doA() } else { doB() if (...) {...} } ίϝϯτ > υΩϡϝϯςʔγϣϯ > Ξϯνύλʔϯ
  13. Ξϯνύλʔϯ: ϓϥΠϕʔτϝϯόΛ࢖ͬͯઆ໌ /** * Returns a string stored in a

    private map [dictionary]. */ fun getDescription(keyword: String): String ίϝϯτ > υΩϡϝϯςʔγϣϯ > Ξϯνύλʔϯ
  14. Ξϯνύλʔϯ: ཁ໿͕ͳ͍ /** * Throws an exception if the given

    `keyword` is empty. */ fun getDescription(keyword: String): String ίϝϯτ > υΩϡϝϯςʔγϣϯ > Ξϯνύλʔϯ
  15. Ξϯνύλʔϯ: ݺͼग़͠ଆʹݴٴ /** * ... * This is called by

    class [UserProfilePresenter]. */ fun getDescription(keyword: String): String ίϝϯτ > υΩϡϝϯςʔγϣϯ > Ξϯνύλʔϯ
  16. υΩϡϝϯςʔγϣϯʹॻ͘಺༰ ཁ໿ (ඞਢ) ʮԿͰ͋Δ͔ɾԿΛ͢Δ͔ʯΛઆ໌͢Δ 1 จɾ 1 ϑϨʔζ ৄࡉ (Φϓγϣφϧ)

    ࢖༻๏΍੍໿౳Λઆ໌͢Δ௥Ճͷจ ίϝϯτ > υΩϡϝϯςʔγϣϯ > ཁ໿
  17. ཁ໿ͷॻ͖ํ: ॏཁͳίʔυ 1/2 ίʔυͰ ࠷΋ॏཁͳ ߦɾϒϩοΫɾཁૉΛݟ͚ͭΔ if (!user.isValid) return val

    rawProfileImage = getProfileImage(user.id, ...) val roundProfileImage = applyRoundFilter(rawProfileImage, ...) profileView.setImage(roundProfileImage) ίϝϯτ > υΩϡϝϯςʔγϣϯ > ཁ໿
  18. ཁ໿ͷॻ͖ํ: ॏཁͳίʔυ 2/2 ࠷΋ॏཁͳ఺Λॻ͖ɺ ิ଍͢Δ /** * Shows a roundly

    cut profile image of a given [user]. * ... */ ... profileView.setImage(roundProfileImage) ཁ໿͸໋໊ͷώϯτʹ΋ͳΔ ! ίϝϯτ > υΩϡϝϯςʔγϣϯ > ཁ໿
  19. ཁ໿ͷॻ͖ํ: ίʔυͷڞ௨఺ ίʔυͷڞ௨఺Λ୳͠ɺ ந৅Խ͢Δ /** * Updates message layout with

    a newly received message data. */ fun ...(receivedMessageData: MessageData) { messageView.text = receivedMessageData.contentText senderNameView.text = receivedMessageData.senderName timestampView.text = receivedMessageData.sentTimeText } ίϝϯτ > υΩϡϝϯςʔγϣϯ > ཁ໿
  20. ཁ໿ͷϑΥʔϚοτ ඪ४ϥΠϒϥϦɾAPI ͷॻ͖ํʹ४ͣΔ ྫ: Kotlin, Java, Swift, Objective-C - Ϋϥε,

    ม਺: ໊ࢺ۟ ྫ: "A generic ordered collection of elements."4 - ؔ਺: ࡾਓশ୯਺ͷ ಈࢺ Ͱ࢝·ΔϑϨʔζ ྫ: "Adds a new element at the end of the array."5 5 https://developer.apple.com/documentation/swift/array/3126937-append 4 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/#list ίϝϯτ > υΩϡϝϯςʔγϣϯ > ཁ໿
  21. ೔ຊޠͷཁ໿ͷϑΥʔϚοτ جຊ͸ӳޠͱಉ͡ - Ϋϥε, ม਺: ओޠΛলུͨ͠ ໊ࢺ ͰऴΘΔจɾମݴࢭΊ ྫ: ʮ...ʹม׵͢ΔΞμϓλͰ͢ɻʯɾʮ...ͷঢ়ଶɻʯ

    - ؔ਺: ओޠΛলུͨ͠ ಈࢺ ͰऴΘΔจ ྫ: ʮ...Λදࣔ͠·͢ɻʯɾʮ...Λฦ͢ɻʯ ܟମɾৗମ ΛϓϩμΫτ಺Ͱ౷Ұ ίϝϯτ > υΩϡϝϯςʔγϣϯ > ཁ໿
  22. υΩϡϝϯςʔγϣϯʹॻ͘಺༰ ཁ໿ (ඞਢ) ʮԿͰ͋Δ͔ɾԿΛ͢Δ͔ʯΛઆ໌͢Δ 1 จɾ 1 ϑϨʔζ ৄࡉ (Φϓγϣφϧ)

    ࢖༻๏΍੍໿౳Λઆ໌͢Δ௥Ճͷจ ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  23. ࢓༷ͱ࢖͍ํ యܕతͳ࢖͍ํ ͱ ظ଴͢Δಈ࡞ ʹ͍ͭͯॻ͘ /** * ... * To

    update view components such as message text and sender name, * give [MessageData] model object to [bindView]. */ class MessageViewPresenter(messageView: View) ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  24. ؔ਺ͷ໭Γ஋ 1/2 ໰୊: ҎԼͷؔ਺ͷ໭Γ஋͸ԿΛҙຯ͢Δ? fun setSelectedState(isSelected: Boolean): Boolean ղ౴ྫ: isToggled

    // ঢ়ଶ͕มԽͨ͠Β true wasSelected // ݺͼग़͠લͷঢ়ଶ isSuccessfullyUpdated // ਖ਼ৗʹऴྃ͢Ε͹ true isSelected // Ҿ਺ `isSelected` Λύεεϧʔ ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  25. ؔ਺ͷ໭Γ஋ 2/2 ؔ਺໊Ͱ͸આ໌͕ෆे෼ͳ৔߹ɺίϝϯτͰิ଍͢Δ - ؔ਺͕ ঢ়ଶΛมߋ͢Δ ৔߹ - ໭Γ஋ʹ ੍໿͕͋Δ

    ৔߹ /** * ... * ... returns true if it was selected before this function call. */ fun setSelectedState(isSelected: Boolean): Boolean ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  26. ؔ਺ͷ໭Γ஋ 2/2 ؔ਺໊Ͱ͸આ໌͕ෆे෼ͳ৔߹ɺίϝϯτͰิ଍͢Δ - ؔ਺͕ ঢ়ଶΛมߋ͢Δ ৔߹ - ໭Γ஋ʹ ੍໿͕͋Δ

    ৔߹ /** * ... * The profile ID is non-negative value. */ fun getProfileId(): Int ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  27. ੍໿ͱΤϥʔ ࣄલ৚݅ ΍ ҧ൓࣌ͷಈ࡞ ʹ͍ͭͯίϝϯτ͢Δ - ؔ਺ݺͼग़࣌͠ͷ Ϩγʔόͷ৚݅ - ࣮Ҿ਺ͷ੍໿

    /** * ... [prepare] must be called before calling [play] or [seek], * or this throws [ResourceNotReadyException]. */ class VideoPlayer(videoPath: String) ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  28. ੍໿ͱΤϥʔ ࣄલ৚݅ ΍ ҧ൓࣌ͷಈ࡞ ʹ͍ͭͯίϝϯτ͢Δ - ؔ਺ݺͼग़࣌͠ͷ Ϩγʔόͷ৚݅ - ࣮Ҿ਺ͷ੍໿

    /** * ... * Returns `null` if the given `position` is out of the array range. */ fun valueAt(position: Int): T? ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  29. ੍໿ͷྫ - ࣮Ҿ਺΍Ϩγʔόͷঢ়ଶ - ΠϯελϯεͷϥΠϑαΠΫϧ - ݺͼग़࣌͠ͷεϨου - ࠶ೖՄೳੑ -

    ܭࢉ࣌ؒɾϝϞϦ࢖༻ྔ - ֎෦؀ڥͷঢ়ଶʢωοτϫʔΫɾ෺ཧετϨʔδ౳ʣ ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  30. ྫ ྫΛ࢖ͬͯ࢓༷ͷཧղΛॿ͚Δ - ࢖͍ํͷྫ - ࣮Ҿ਺ͱ໭Γ஋ͷྫ /** * ... For

    example, this returns `listOf("a", "bc", "", "d")` * for argument `"a, bc ,,d"` */ fun splitByComma(string: String): List<String> {... ίϝϯτ > υΩϡϝϯςʔγϣϯ > ৄࡉ
  31. υΩϡϝϯςʔγϣϯ: ·ͱΊ ର৅: Ϋϥεɾؔ਺ɾม਺ɾείʔϓ ಺༰: ཁ໿ (ඞਢ)ɾ ৄࡉ(೚ҙ) ஫ҙ఺: -

    ໊લͰ͸આ໌͕ෆे෼ͳͱ͖ʹॻ͘ - ݺͼग़͠ଆ΍࣮૷ͷৄࡉʹݴٴ͠ͳ͍ ίϝϯτ > υΩϡϝϯςʔγϣϯ > ·ͱΊ
  32. ඇܗࣜతͳίϝϯτ: Kotlin ʹ͓͚Δྫ // Function explanation, which won't appear on

    KDoc fun function(param /* Parameter explanation */: Param) { // Code block summary someMethod(value) // Reason of a statement newId = param.id + 123 /* Reason of constant value */ + ... ... - ಡΉਓͷॿ͚ʹͳΔ͜ͱͳΒԿͰ΋ॻ͘ - ཁ໿͸ඞਢͰ͸ͳ͍ ίϝϯτ > ඇܗࣜతͳίϝϯτ
  33. େ͖ͳίʔυʹର͢Δίϝϯτ ίϝϯτͰ ίʔυͷ·ͱ·Γ Λ࡞Γɺ֓ཁΛઆ໌͢Δ ... val messageKey = ... val

    messageData = messageCache[messageKey] ... if (messageData == null || ...) { // <- When this satisfies? ... // <- Hard to overview code in a nest } ίϝϯτ > ඇܗࣜతͳίϝϯτ
  34. େ͖ͳίʔυʹର͢Δίϝϯτ ίϝϯτͰ ίʔυͷ·ͱ·Γ Λ࡞Γɺ֓ཁΛઆ໌͢Δ // Get message data cache if

    it's available val messageKey = ... val messageData = messageCache[messageKey] ... // Load message data from DB if there's no cached data. if (messageData == null || ...) { ... } ίϝϯτ > ඇܗࣜతͳίϝϯτ
  35. ෳࡶɾඇ௚ײతͳίʔυʹର͢Δίϝϯτ ཧղ͕೉͍͠ίʔυͷ֓ཁ΍ཧ༝Λઆ໌͢Δ // // wordReplacementData.reverse() .forEach { (startIndex, endIndex, newText)

    -> stringBuilder.replace(startIndex, endIndex, newText) } ໰୊: ͳͥ reverse() Λݺͼग़͢ඞཁ͕͋Δʁ ίϝϯτ > ඇܗࣜతͳίϝϯτ
  36. ෳࡶɾඇ௚ײతͳίʔυʹର͢Δίϝϯτ ཧղ͕೉͍͠ίʔυͷ֓ཁ΍ཧ༝Λઆ໌͢Δ // Replace texts in the reverse order because

    `replace()` // affects the following indices. wordReplacementData.reverse() .forEach { (startIndex, endIndex, newText) -> stringBuilder.replace(startIndex, endIndex, newText) } ؒҧͬͨʮϦϑΝΫλϦϯάʯʹΑΔ reverse() ͷ࡟আΛ๷͙ ίϝϯτ > ඇܗࣜతͳίϝϯτ
  37. ϫʔΫΞϥ΢ϯυΛؚΉίʔυʹର͢Δίϝϯτ ϫʔΫΞϥ΢ϯυͰԿΛ͢Δ͔ɾͳͥඞཁͳͷ͔Λઆ໌͢Δ // We restore the previous state here //

    because libraryFunction() may break the receiver state ৄࡉΛ஌ΔͨΊͷϦϯΫΛ௥Ճ͢Δ // To avoid Device-X specific tinting bug (see, ISSUE-123456) ίϝϯτ > ඇܗࣜతͳίϝϯτ
  38. ·ͱΊ - ίϝϯτ͸ඞཁͳͱ͖ɺඞཁͳ͚ͩॻ͘ - ίϝϯτΛॻ͘લޙͰϦϑΝΫλϦϯάΛߦ͏ - υΩϡϝϯςʔγϣϯ: ཁ໿ (ඞਢ) ɾ

    ৄࡉ (Φϓγϣφϧ) - ඇܗࣜతͳίϝϯτ: େ͖ͳɾෳࡶͳɾඇ௚ײతͳίʔυͷઆ໌ ίϝϯτ > ·ͱΊ