390

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

---

セッションリスト

---

- https://gihyo.jp/book/2022/978-4-297-13036-7

© 2019-2023 Munetoshi Ishikawa, supported by LINE corporation

May 11, 2023

## Transcript

2. ### લճͷ͓͞Β͍: ίϝϯτ - ίϝϯτ͸ඞཁͳͱ͖ɺඞཁͳ͚ͩॻ͘ - ίϝϯτΛॻ͘લޙͰϦϑΝΫλϦϯάΛߦ͏ - υΩϡϝϯςʔγϣϯ: ཁ໿ (ඞਢ)

ɾ ৄࡉ (Φϓγϣφϧ) - ඇܗࣜతͳίϝϯτ: େ͖ͳɾෳࡶͳɾඇ௚ײతͳίʔυͷઆ໌
3. ### ߨٛͷߏ੒ - ಋೖͱݪଇ - ࣗવݴޠ: ໋໊, ίϝϯτ - ܕͷߏ଄: ঢ়ଶ,

ؔ਺ - ܕؒͷߏ଄: ґଘؔ܎I, ґଘؔ܎II - ϨϏϡʔ ঢ়ଶ > ಋೖ
4. ### ίʔυͷ࣮ߦͱঢ়ଶ ίʔυͷධՁɾ࣮ߦͰ ঢ়ଶ͸มΘΓಘΔ var i = 1 println(i) // prints

`"1"` i = i + 1 println(i) // prints `"2"` ঢ়ଶ > ಋೖ

ಋೖ
6. ### ঢ়ଶͱίʔυͷಡΈ΍͢͞ͷؔ܎ 2/2 ೋ෼໦ͷ෯༏ઌ୳ࡧͷ࣮૷Λൺֱ 5 6 8 3 1 9 5

node value ঢ়ଶ > ಋೖ
7. ### ঢ়ଶͱίʔυͷಡΈ΍͢͞ͷؔ܎ 2/2 ೋ෼໦ͷ෯༏ઌ୳ࡧͷ࣮૷Λൺֱ class Node(val value: Int, val left: Node?,

val right: Node?) Case 1: ՄมͳΩϡʔΛར༻ Case 2: ࠶ؼݺͼग़͠Λར༻ ঢ়ଶ > ಋೖ
8. ### 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 } ঢ়ଶ > ಋೖ
9. ### 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) } ঢ়ଶ > ಋೖ
10. ### ࠶ؼݺͼग़͠ͷྫͷ໰୊఺ ΑΓෳࡶ - ࠶ؼ͢Δؔ਺ͱΠϯλʔϑΣΠεͷ෼཭͕ඞཁ - ʮ࣍ͷΩϡʔͷঢ়ଶʯͷԋࢉ͕ෳࡶ valueToFind ͕มΘΒͳ͍͜ͱ͕ະอূ - ࠶ؼݺͼग़͠Ͱ࣮Ҿ਺͕มΘΔ͔ෆ໌

- ࣮Ҿ਺Λݻఆ͢Δʹ͸ϩʔΧϧؔ਺͕ඞཁ ঢ়ଶ > ಋೖ
11. ### ঢ়ଶͱಡΈ΍͢͞ͷؔ܎Ͱ஫ҙ͢Δ΂͖఺ ʮεςʔτϨεɾෆมɾࢀরಁաͳίʔυʯࣗମ͕໨తͰ͸ͳ͍ - ಡΈ΍͢͞ɾؤ݈ੑΛ࣮ݱ͢Δखஈ - ࠶ؼݺͼग़͠΍৞ΈࠐΈ͸ίʔυΛվળ͢ΔͨΊʹ࢖͏ ݟ͔͚ͷঢ়ଶ਺ΑΓ΋ϓϩάϥϜ࣮ߦঢ়ଶશମʹ஫໨͢Δ - ہॴతͳՄมੑ͸େ͖ͳ໰୊Ͱ͸ͳ͍ -

ہॴతͳෆมੑ͕શମͷঢ়ଶ਺Λ૿΍͢͜ͱ΋͋Δ ঢ়ଶ > ಋೖ

ม਺ؒͷؔ܎
15. ### ௚ަͷؔ܎: ྫ αʔϏε಺௨՟ʮcoinʯͷදࣔը໘Λ૝ఆ ABC 123,456 coins 100,000 1,000 1,000 8

10 11 / / / 5 5 5 ঢ়ଶ > ม਺ؒͷؔ܎
16. ### ௚ަͷؔ܎: ྫ αʔϏε಺௨՟ʮcoinʯͷදࣔը໘Λ૝ఆ ௚ަ: coinCount ͱ coinHistoryVisibility coinCount ͷ஋͸ coinHistoryVisibility

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

ෆਖ਼ͳঢ়ଶͷݪҼʹͳΔ CoinState(coinCount = 10, coinText = "0 coin") ঢ়ଶ > ม਺ؒͷؔ܎

20. ### ؔ਺΍ήολʔϓϩύςΟʹΑΔஔ͖׵͑ 1/2 coinText ͸ coinCount ͔ΒٻΊΒΕΔ class CoinState( val coinCount:

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

Int ) { fun coinText(formatter: Formatter): String = formatter.getQuantityString(coinCount, "coin") } ෆਖ਼ͳ CoinState ͷΠϯελϯε͸ଘࡏ͑͠ͳ͍ ঢ়ଶ > ม਺ؒͷؔ܎ > ؔ਺΍ήολʔϓϩύςΟʹΑΔஔ͖׵͑

> ௚࿨ܕΛ࢖͏
24. ### ௚࿨ܕΛ࢖͏ 2/3 resultText ͱ errorCode ͷͲͪΒ͔Ұํ͚͕ͩ non-null class QueryResponse( val

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

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

QueryResponse() class Error(val errorCode: Int): QueryResponse() } QueryResponse ͸ resultText ͔ errorCode Λഉଞతʹ࣋ͭ ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
27. ### ඇ௚ަͳؔ܎Λղܾ͢Δํ๏ - ؔ਺΍ήολʔϓϩύςΟʹஔ͖׵͑Δ - ௚࿨ܕΛ࢖͏ - খ͞ͳΫϥεͰ୅༻͢Δ - ಛघܥͱͯ͠ྻڍܕΛ࢖͏ ঢ়ଶ

> ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏

> ௚࿨ܕΛ࢖͏
29. ### ௚࿨ܕͷ୅༻ 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) { ... } ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
30. ### ඇ௚ަͳؔ܎Λղܾ͢Δํ๏ - ؔ਺΍ήολʔϓϩύςΟʹஔ͖׵͑Δ - ௚࿨ܕΛ࢖͏ - খ͞ͳΫϥεͰ୅༻͢Δ - ಛघܥͱͯ͠ྻڍܕΛ࢖͏ ঢ়ଶ

> ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
31. ### ಛघܥͱͯ͠ͷྻڍܕ ඇ௚ަͳؔ܎ͷഉআʹྻڍܕͰे෼ͳ͜ͱ΋ // isResultViewShown && isErrorViewShown can't happen var isResultViewShown:

Boolean var isErrorViewShown: Boolean ྻڍʹΑͬͯ (true, true) ͷ৔߹Λഉআ͢Δ enum class VisibleViewType { RESULT_VIEW, ERROR_VIEW, NOTHING } ঢ়ଶ > ม਺ؒͷؔ܎ > ௚࿨ܕΛ࢖͏
32. ### ม਺ؒͷؔ܎: ·ͱΊ ௚ަɾඇ௚ަͷؔ܎ - ஋ͷऔΕΔൣғ͕Өڹ͢Δ͔Ͳ͏͔ - ʮෆਖ਼ͳঢ়ଶʯΛ࡞Βͳ͍ͨΊʹॏཁ ඇ௚ަͷؔ܎ͷഉআํ๏ - ؔ਺ɾήολʔϓϩύςΟ

- ௚࿨ܕɾྻڍܕɾখ͞ͳΫϥε ঢ়ଶ > ม਺ؒͷؔ܎ > ·ͱΊ

34. ### ঢ়ଶભҠͷछྨ - ෆม: ఆ਺ ౳ - ႈ౳: Closable, Lazy ౳

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

- ඇ८ճ (ࣗݾϧʔϓΛআ͘): ϦιʔεͷετϦʔϜ ౳ - ८ճ: ࠶ར༻ՄೳΦϒδΣΫτ ౳ ঢ়ଶ > ঢ়ଶભҠͷઃܭ
36. ### ෆมͳΦϒδΣΫτ ͢΂ͯͷϓϩύςΟ͕มԽ͠ͳ͍ // 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) ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ෆม
37. ### ෆมɾՄมͷ஫ҙ఺ 1/3 Ұ෦ͷϓϩύςΟ͚ͩͰ΋ෆมʹͰ͖ͳ͍͔ݕ౼͢Δ class CheckBoxViewModel( val text: String, var isEnabled:

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

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

itemModelList.clear() or itemModelList = emptyMutableList() } ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ෆม
40. ### ෆมɾՄมͷ஫ҙ఺ 3/3 ࢀরͱࢀরઌΦϒδΣΫτΛಉ࣌ʹՄมʹ͠ͳ͍ʢՄೳͳݶΓʣ var itemModelList: MutableList<ItemModel> val list = itemModelList

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

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

closable.close() // "Closed" state closable.close() // Valid. Keep "Closed" state ෭࡞༻ΛӅṭͰ͖Δ͜ͱ͕͋Δ (ྫ: Kotlin ͷ Lazy) ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ႈ౳

> ႈ౳
44. ### ႈ౳ͷར఺ ෆਖ਼ͳঢ়ଶભҠͷഉআ // We may forget to check `isClosed` if

(!nonIdempotentClosable.isClosed()) { nonIdempotentClosable.close() } // We can simply call `close` for an idempotent instance idempotentClosable.close() ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ႈ౳

ႈ౳

ႈ౳
47. ### ঢ়ଶભҠͷछྨ - ෆม: ఆ਺ ౳ - ႈ౳: Closable, Lazy ౳

- ඇ८ճ (ࣗݾϧʔϓΛআ͘): ϦιʔεͷετϦʔϜ ౳ - ८ճ: ࠶ར༻ՄೳΦϒδΣΫτ ౳ ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
48. ### ඇ८ճͱ८ճ 1/2 ඇ८ճ: Ҏલͷঢ়ଶʹ໭Δ ܦ࿏͕ͳ͍ ८ճ: Ҏલͷঢ়ଶʹ໭Δ ܦ࿏͕͋Δ State1 State2

State3 State1 State2 State3 ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
49. ### ඇ८ճͱ८ճ 2/2 (ࣗݾϧʔϓΛআ͍ͯ) ඇ८ճ͕๬·͍͠ ΦϒδΣΫτΛ࢖͍ࣺͯΔ - ঢ়ଶΛϦηοτ͢Δ͝ͱʹ৽ͨͳΠϯελϯεΛ࡞Δ - ύϑΥʔϚϯεͰෆརͳ͜ͱ΋ //

This class instance works only a given `videoPath`. class VideoPlayer(videoPath: String) { enum class State { LOADING, PLAYING, FINISHED, ERROR } ... ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ

> ८ճɾඇ८ճ
51. ### ඇ८ճͷྫɹ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() ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
52. ### ८ճͷྫɹ1/2 startMeasurement ʹΑΓ࠶ར༻Մೳ Measuring Stopped finishMeasurement finishMeasurement startMeasurement startMeasurement initial

state ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
53. ### ८ճͷྫɹ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 ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
54. ### ८ճͷ໰୊఺ ࢖͍ํΛؒҧ͍΍͍͢ private val logger = DurationLogger(...) fun runSomeHeavyTask() {

logger.startMeasurement() ... runAnotherHeavyTask() // Bug: `logger` is touched internally logger.finishMeasurement() } private fun runAnotherHeavyTask() { /* Use `logger` here, too */ } ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
55. ### ඇ८ճͷঢ়ଶભҠͱݱ࣮ Ϟσϧͷ୯७ԽͷͨΊʹ८ճ͕ඞཁʹͳΔ - ແཧͳ८ճͷ࡟আ͸ίʔυΛΑΓෳࡶʹ͢Δ͔΋ - DurationLogger ͷྫ: Measuring <-> Paused

८ճͷൣғΛখ͘͢͞Δ ঢ়ଶ > ঢ়ଶભҠͷઃܭ > ८ճɾඇ८ճ
56. ### ८ճͷہॴԽ ८ճΛ 1 ͭͷঢ়ଶʹด͡ࠐΊΔ = େ͖ͳ८ճΛ࡞Βͳ͍ Measuring Finished Paused finish

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

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

·ͱΊ

ঢ়ଶ > ·ͱΊ