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

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

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

第 4 回: 状態

---

セッションリスト

第 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. ঢ়ଶͱίʔυͷಡΈ΍͢͞ͷؔ܎ 2/2 ೋ෼໦ͷ෯༏ઌ୳ࡧͷ࣮૷Λൺֱ class Node(val value: Int, val left: Node?,

    val right: Node?) Case 1: ՄมͳΩϡʔΛར༻ Case 2: ࠶ؼݺͼग़͠Λར༻ ঢ়ଶ > ಋೖ
  3. Case 1: ՄมͳΩϡʔΛར༻ fun search(valueToFind: Int, root: Node): Node? {

    val queue = ArrayDeque<Node>() var node: Node? = root while (node != null && node.value != valueToFind) { node.left?.let { queue.add(it) } node.right?.let { queue.add(it) } node = queue.poll() } return node } ঢ়ଶ > ಋೖ
  4. Case 2: ࠶ؼݺͼग़͠Λར༻ fun search(valueToFind: Int, root: Node): Node? =

    innerSearch(valueToFind, listOf(root)) tailrec fun innerSearch(valueToFind: Int, queue: List<Node>): Node? { val node = queue.getOrNull(0) if (node == null || node.value == valueToFind) { return node } val nextQueue = queue.subList(1, queue.size) + (node.left?.let { listOf(it) } ?: emptyList()) + (node.right?.let { listOf(it) } ?: emptyList()) return innerSearch(valueToFind, nextQueue) } ঢ়ଶ > ಋೖ
  5. ௚ަͷؔ܎: ྫ αʔϏε಺௨՟ʮcoinʯͷදࣔը໘Λ૝ఆ ௚ަ: coinCount ͱ coinHistoryVisibility coinCount ͷ஋͸ coinHistoryVisibility

    ʹؔ܎͠ͳ͍ ඇ௚ަ: coinCount ͱ coinText coinCount ͱ coinText ͷऔΕΔ஋ͷ૊Έ߹Θ͕ͤݶΒΕΔ ঢ়ଶ > ม਺ؒͷؔ܎
  6. ௚ަੑʹ͓͚Δجຊํ਑ ʮඇ௚ަʯͳؔ܎Λආ͚Δ ඇ௚ަͳؔ܎ͷ͋ΔΫϥεͷྫ class CoinState(val coinCount: Int, val coinText: String)

    ෆਖ਼ͳঢ়ଶͷݪҼʹͳΔ CoinState(coinCount = 10, coinText = "0 coin") ঢ়ଶ > ม਺ؒͷؔ܎
  7. ؔ਺΍ήολʔϓϩύςΟʹΑΔஔ͖׵͑ 1/2 coinText ͸ coinCount ͔ΒٻΊΒΕΔ class CoinState( val coinCount:

    Int, val coinText: String ) coinCount ͷΈΛࢦఆՄೳʹ͢Δ ঢ়ଶ > ม਺ؒͷؔ܎ > ؔ਺΍ήολʔϓϩύςΟʹΑΔஔ͖׵͑
  8. ؔ਺΍ήολʔϓϩύςΟʹΑΔஔ͖׵͑ 2/2 coinText Λ coinCount ͔ΒٻΊΔ class CoinState( val coinCount:

    Int ) { fun coinText(formatter: Formatter): String = formatter.getQuantityString(coinCount, "coin") } ෆਖ਼ͳ CoinState ͷΠϯελϯε͸ଘࡏ͑͠ͳ͍ ঢ়ଶ > ม਺ؒͷؔ܎ > ؔ਺΍ήολʔϓϩύςΟʹΑΔஔ͖׵͑
  9. ௚࿨ܕΛ࢖͏ 2/3 resultText ͱ errorCode ͷͲͪΒ͔Ұํ͚͕ͩ non-null class QueryResponse( val

    resultText: String?, val errorCode: Int? ) ҰํͷϓϩύςΟ͸΋͏Ұํ͔ΒٻΊΒΕͳ͍ ഉଞతͳ஋Λ௚࿨ܕͰදݱ͢Δ ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
  10. ௚࿨ܕ ෳ਺ͷܕͷ஋ͷத͔Βͪΐ͏Ͳ 1 ͭͷ஋ΛऔΔܕ ू߹࿦ͷ௚࿨ू߹ʹ૬౰ QueryResponse ͷྫ: QueryResponse = Result

    | Error ࣮ݱखஈ: Tagged union, variant, sealed class, associated value ... ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
  11. ௚࿨ܕΛ࢖͏ 3/3 sealed class QueryResponse { class Result(val resultText: String):

    QueryResponse() class Error(val errorCode: Int): QueryResponse() } QueryResponse ͸ resultText ͔ errorCode Λഉଞతʹ࣋ͭ ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
  12. ௚࿨ܕͷ୅༻ 2/2 class QueryResponse { @Nullable private final String resultText;

    @Nullable private final Integer errorCode; private QueryResponse(...) { ... } @NonNull static QueryResponse asResult(@NonNull String ...) { ... } @NonNull static QueryResponse asError(int errorCode) { ... } ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
  13. ಛघܥͱͯ͠ͷྻڍܕ ඇ௚ަͳؔ܎ͷഉআʹྻڍܕͰे෼ͳ͜ͱ΋ // isResultViewShown && isErrorViewShown can't happen var isResultViewShown:

    Boolean var isErrorViewShown: Boolean ྻڍʹΑͬͯ (true, true) ͷ৔߹Λഉআ͢Δ enum class VisibleViewType { RESULT_VIEW, ERROR_VIEW, NOTHING } ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
  14. ঢ়ଶભҠͷछྨ - ෆม: ఆ਺ ౳ - ႈ౳: Closable, Lazy ౳

    - ඇ८ճ (ࣗݾϧʔϓΛআ͘): ϦιʔεͷετϦʔϜ ౳ - ८ճ: ࠶ར༻ՄೳΦϒδΣΫτ ౳ ঢ়ଶ > ঢ়ଶભҠͷઃܭ
  15. ঢ়ଶભҠͷछྨ - ෆม: ఆ਺ ౳ - ႈ౳: Closable, Lazy ౳

    - ඇ८ճ (ࣗݾϧʔϓΛআ͘): ϦιʔεͷετϦʔϜ ౳ - ८ճ: ࠶ར༻ՄೳΦϒδΣΫτ ౳ ঢ়ଶ > ঢ়ଶભҠͷઃܭ
  16. ෆมͳΦϒδΣΫτ ͢΂ͯͷϓϩύςΟ͕มԽ͠ͳ͍ // Examples of "immutable" class Immutable(val value: Int)

    class AnotherImmutable(val immutable: Immutable) // Examples of "mutable" class Mutable(var value: Int) class AnotherMutable(var immutable: Immutable) class YetAnotherMutable(val mutable: Mutable) ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ෆม
  17. ෆมɾՄมͷ஫ҙ఺ 1/3 Ұ෦ͷϓϩύςΟ͚ͩͰ΋ෆมʹͰ͖ͳ͍͔ݕ౼͢Δ class CheckBoxViewModel( val text: String, var isEnabled:

    Boolean ) ஋ͷϥΠϑαΠΫϧ͝ͱʹΫϥεΛ෼͚Δ͜ͱ΋ߟྀ͢Δ ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ෆม
  18. ෆมɾՄมͷ஫ҙ఺ 2/3 ෆมͱಡΈऔΓઐ༻ͷҧ͍ʹ஫ҙ͢Δ val mutableList: MutableList<ItemModel> = mutableListOf() val list:

    List<ItemModel> = mutableList println(list.size) // => "0" mutableList += itemModel println(list.size) // => "1" ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ෆม
  19. ෆมɾՄมͷ஫ҙ఺ 3/3 ࢀরͱࢀরઌΦϒδΣΫτΛಉ࣌ʹՄมʹ͠ͳ͍ʢՄೳͳݶΓʣ var itemModelList: MutableList<ItemModel> val list = itemModelList

    fun clearItemModelList() { // itemModelList.clear() or itemModelList = emptyMutableList() } มߋͷӨڹൣғ͕Θ͔Γʹ͘͘ͳΔ ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ෆม
  20. ঢ়ଶભҠͷछྨ - ෆม: ఆ਺ ౳ - ႈ౳: Closable, Lazy ౳

    - ඇ८ճ (ࣗݾϧʔϓΛআ͘): ϦιʔεͷετϦʔϜ ౳ - ८ճ: ࠶ར༻ՄೳΦϒδΣΫτ ౳ ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ႈ౳
  21. ႈ౳ੑ ʮ1 ճʯͱʮෳ਺ճʯͰ࣮ߦ݁Ռ͕౳͍͠ val closable = Closable() // "Open" state

    closable.close() // "Closed" state closable.close() // Valid. Keep "Closed" state ෭࡞༻ΛӅṭͰ͖Δ͜ͱ͕͋Δ (ྫ: Kotlin ͷ Lazy) ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ႈ౳
  22. ႈ౳ͷར఺ ෆਖ਼ͳঢ়ଶભҠͷഉআ // We may forget to check `isClosed` if

    (!nonIdempotentClosable.isClosed()) { nonIdempotentClosable.close() } // We can simply call `close` for an idempotent instance idempotentClosable.close() ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ႈ౳
  23. ঢ়ଶભҠͷछྨ - ෆม: ఆ਺ ౳ - ႈ౳: Closable, Lazy ౳

    - ඇ८ճ (ࣗݾϧʔϓΛআ͘): ϦιʔεͷετϦʔϜ ౳ - ८ճ: ࠶ར༻ՄೳΦϒδΣΫτ ౳ ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
  24. ඇ८ճͱ८ճ 2/2 (ࣗݾϧʔϓΛআ͍ͯ) ඇ८ճ͕๬·͍͠ ΦϒδΣΫτΛ࢖͍ࣺͯΔ - ঢ়ଶΛϦηοτ͢Δ͝ͱʹ৽ͨͳΠϯελϯεΛ࡞Δ - ύϑΥʔϚϯεͰෆརͳ͜ͱ΋ //

    This class instance works only a given `videoPath`. class VideoPlayer(videoPath: String) { enum class State { LOADING, PLAYING, FINISHED, ERROR } ... ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
  25. ඇ८ճͷྫɹ2/2 class DurationLogger(...) { private var state: State = State.Measuring(...)

    fun finishMeasureMent() { val measuringState = state as? State.Measuring ?: return ... state = State.Finished } private sealed class State { class Measuring(val startedTimeInNanos: Long) : State() object Finished : State() ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
  26. ८ճͷྫɹ2/2 class DurationLogger(...) { private var state: State = State.Stopped

    fun startMeasurement() { if (state == State.Stopped) { state = State.Measuring(...) } } fun finishMeasurement() { val measuringState = state as? State.Measuring ?: return ... state = State.Stopped ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
  27. ८ճͷ໰୊఺ ࢖͍ํΛؒҧ͍΍͍͢ private val logger = DurationLogger(...) fun runSomeHeavyTask() {

    logger.startMeasurement() ... runAnotherHeavyTask() // Bug: `logger` is touched internally logger.finishMeasurement() } private fun runAnotherHeavyTask() { /* Use `logger` here, too */ } ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
  28. ८ճͷہॴԽ ८ճΛ 1 ͭͷঢ়ଶʹด͡ࠐΊΔ = େ͖ͳ८ճΛ࡞Βͳ͍ Measuring Finished Paused finish

    pause resume Measuring/Paused ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
  29. ঢ়ଶભҠͷछྨ - ෆม: ఆ਺ ౳ - ႈ౳: Closable, Lazy ౳

    - ඇ८ճ (ࣗݾϧʔϓΛআ͘): ϦιʔεͷετϦʔϜ ౳ - ८ճ: ࠶ར༻ՄೳΦϒδΣΫτ ౳ ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ·ͱΊ