SUGARのアーキテクチャー / SUGAR ARCHITECTURE 20190425

2febceae473e4e2a12b6f92314cd2d8a?s=47 sugitani
April 25, 2019

SUGARのアーキテクチャー / SUGAR ARCHITECTURE 20190425

2019/4/25に行われた「突撃!!隣のアーキテクチャ」
https://totsugeki-architecture.connpass.com/event/125845/
での発表資料です

2febceae473e4e2a12b6f92314cd2d8a?s=128

sugitani

April 25, 2019
Tweet

Transcript

  1. 46("3ͷΞʔΩςΫνϟ ಥܸʂʂྡͷΞʔΩςΫνϟ !TVHJUBOJ " 3 $ ) * 5 &

    $ 5 6 3 &
  2. લઆ

  3. ࣗݾ঺հ • ਿ୩ͱਃ͠·͢ • ܦྺ • υϫϯΰ χίχίੜ์ૹ • ηϓςʔχɾΦϦδφϧ

    $50 ("/." • 46("3$50 46("3 !TVHJUBOJ
  4. 46("3ͱ͸ʁ • ஶ໊ਓʹಛԽͨ͠ϥΠϒ഑৴Ξ ϓϦ • ొ࿥ͯ͠ΞʔςΟετΛϑΥϩʔ͠ ͓ͯ͘ͱి࿩ $BMM,JU ͕དྷΔ •

    Ұఆ਺͕ϐοΫΞοϓ͞Εબ୒ΤϦ ΞʹೖΔʢ௨শ͓ण࢘Ϩʔϯʣ • ΞʔςΟετ͕௨࿩ର৅ΛબͿͱ௨ ࿩͕Ͱ͖Δ ଞͷਓ͸ͦͷ༷ࢠΛ ோΊΔ • ഑৴ऀ͕εΰ͘ͳ͚Ε͹੒Γཱ ͨͳ͍ • ഑৴ऀ͕Ϡό͍
  5. ΞʔΩςΫνϟ঺հ͸͡Ί·͢ ˞͝஫ҙ˞ %%%ͳͲ͍ΖΜͳ୯ޠ͕Ͱ͖ͯ·͕͢ ຊϓϨθϯςʔγϣϯ͸ฐࣾͷݱঢ়ͷ঺հʹա͗ͣ Կ͔͠Βͷํ๏࿦ͷਖ਼͍࣮͠ફํ๏Λࣔ͢΋ͷͰ͸͋Γ·ͤΜ

  6. ࠾༻ΞʔΩςΫνϟͱ঺հൣғ 4DBMBαʔό 4XJGUJ04 ,PUMJO"OESPJE ͥΜͿ΍Δ γεςϜΞʔΩςΫνϟ %*1Λ׆༻͢ΔҰ೿ʁ γεςϜΞʔΩςΫνϟ %*1Λ׆༻͢ΔҰ೿ʁ γεςϜΞʔΩςΫνϟ

    %*1Λ׆༻͢ΔҰ೿ʁ (6*ΞʔΩςΫνϟ ແ͠ +40/ग़͚ͩ͢ (6*ΞʔΩςΫνϟ .7$͔ͳ͊ʜʁ (6*ΞʔΩςΫνϟ .77.ͩͱࢥͬͯΔ
  7. ͲΕ΋جຊతʹߏ଄͸Ұॹ TSD EPNBJO BQQMJDBUJPO BEBQUFS WJFX *OGSBTUSVDUVSF

  8. Πϝʔδ͸͓ͳ͡Έͷԁܗ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF

  9. %PNBJO %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF %%%Ͱੜ੒͞ΕΔ֓೦ͷॅ·͏৔ॴ &OUJUZ7BMVF0CKFDU4FSWJDF 3FQPTJUPSZ౳ͷఆٛ

    USBJUJOUFSGBDFQSPUPDPM Λஔ͘
  10. "QQMJDBUJPO %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF υϝΠϯ૚ͷొ৔ਓ෺Λ ҙຯͷ͋Δॲཧʹ·ͱΊΔΫϥεͷॅ·͏৔ॴ *OKFDU͞Εͨ%PNBJOΠϯελϯεΛ࢖ͬͯͻͱ·ͱ·Γ

    ͷॲཧʢྫσʔλΛ3FQPTJUPSZ͔ΒݺΜͰ4FSWJDFʹ௨ ͢ͳͲՃ޻͔ͯ͠Βอଘ͢Δ Λߦ͏࣮૷͔ఆٛΛஔ͘
  11. "EBQUFS %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF υϝΠϯ૚ͷ࣮૷ͷॅ·͏৔ॴ υϝΠϯ૚ͷఆٛΛܧঝ࣮ͨ͠૷ʢ%#ʹ ΞΫηε͢Δ3FQPTJUPSZ౳ʣΛஔ͘

  12. 7JFX ͦͷଞʣ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF 6*ͱίϯτϩʔϥ 6*7JFX$POUSPMMFS6*7JFX"DUJWJUZ'SB

    HNFOU QMBZͷ $POUSPMMFSͳͲݟͨ໨ ͱૢ࡞ͷ࢝·Γ͔ΒऴΘΓ·Ͱͷ੍ޚʹ ؔΘΔ࣮૷Λஔ͖·͢ɻ
  13. *OGSBTUSVDUVSF %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF ൚༻తͳ΋ͷஔ͖৔ จࣈྻ΍࣌ࠁૢ࡞ϢʔςΟϦςΟɺ"84 ͷԿ͔͠ΒΛ࢖͍΍͘͢ϥοϓͨ͠΋ͷ

    ౳ͷͲ͜Ͱ΋࢖͑ͦ͏ͳ࣮૷Λஔ͘ɻ
  14. ಺ଆ͚ͩར༻Ͱ͖Δ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF ґଘੑٯసͷݪଇΛ׆༻ %PNBJOr 7JFXɺ"EBQUFSɺ"QQMJDBUJPOΛར༻

    JNQPSU Ͱ͖ͳ͍ "QQMJDBUJPOr %PNBJOΛར༻Ͱ͖Δɻ 7JFXɺ"EBQUFSΛར༻Ͱ͖ͳ͍ "EBQUFSr"QQMJDBUJPOɺ%PNBJOΛར༻Ͱ͖Δɻ7JFXΛར༻Ͱ͖ͳ͍ 7JFXr "EBQUFSɺ"QQMJDBUJPOɺ%PNBJOΛར༻Ͱ͖Δ *OGSBTUSVDUVSFr ଞΛར༻Ͱ͖ͳ͍ɻଞ͔Βར༻͞ΕΔ
  15. %PNBJO૚ͷਂ۷Γ

  16. %PNBJO૚ͷํ਑ • υϝΠϯۦಈઃܭͰϞσϦϯάΛߦ͏ • &SJDຊʹͰ͖Δ͚ͩ४ڌͯ͠ݕ౼͢Δ • ੠ʹग़ͯٞ͠࿦͢Δ • ݪଇఆٛ USBJUJOUFSGBDFQSPUPDPM

    Λஔ͘ • γϯϓϧͳ7BMVF0CKFDUʹ͸ྫ֎΋͋Δ • ط੒ͷ೔෇Ϋϥε΍63-ΫϥεΛ͔ͭͬͨΓ • UZQFBMJBT'PP4USJOH TXJGULPUMJO ΍"OZ7BM TDBMB Ͱදݱͨ͠Γ • &OVN࢖ͬͨΓ • ಉ͡ଘࡏʹରͯ͠αʔόͱΫϥΠΞϯτͰಉ͡ݴ༿΍ݟ͑ํʹ ͳΔͱ͸ݶΒͳ͍͜ͱʹؾΛ͚ͭΔ
  17. ʮΞΧ΢ϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4DBMB trait EntityId { /* … */ }

    trait Entity[T <: EntityId] { val id: T } trait UserId extends EntityId trait ImageResource { /* … */ } case class UserName(value: String) extends AnyVal trait RegisteredUser extends Entity[UserId]{ val name: UserName val icon: ImageResource val registeredDate: LocalDateTime }
  18. ʮΞΧ΢ϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4DBMB trait EntityId { /* … */ }

    trait Entity[T <: EntityId] { val id: T } trait UserId extends EntityId trait ImageResource { /* … */ } case class UserName(value: String) extends AnyVal trait RegisteredUser extends Entity[UserId]{ val name: UserName val icon: ImageResource val registeredDate: LocalDateTime } ొ࿥ࡁΈͰͳ͍Ϣʔβʔ͍ͩͬͯΔͷҙ
  19. trait Entity { type ID <: EntityId; val id: ID

    } case class User(id: UserId, …) extends Entity { type ID = UserId } 5JQTɿ&OUJUZͱ*% ར༻ྫ ͕ͧ͘͞Μᐌ͘ ҙຯ ৽نͰ࡞Δ৔߹͸ͪ͜Βʹ͓͖ͯ͠·͠ΐ͏ʂ https://twitter.com/gakuzzzz/status/1103240324120403968
  20. ʮΞΧ΢ϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4XJGU protocol EntityId { /* … */ }

    protocol Entity { var entityId: EntityId { get } } protocol UserId: EntityId { } typealias UserName = String protocol Profile: Entity { var userId: UserId { get } var name: UserName { get } var icon: URL? { get } } extension Profile { var entityId: EntityId { return userId } }
  21. ʮΞΧ΢ϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4XJGU protocol EntityId { /* … */ }

    protocol Entity { var entityId: EntityId { get } } protocol UserId: EntityId { } typealias UserName = String protocol Profile: Entity { var userId: UserId { get } var name: UserName { get } var icon: URL? { get } } extension Profile { var entityId: EntityId { return userId } } ΫϥΠΞϯτ͔Β͢Ε͹ਓ͡Όͳͯ͘ ϓϩϑΟʔϧ͔͠ڵຯͳ͍ΑͶɺ ͷҙΛࠐΊ໋໊ͨ
  22. ʮΞΧ΢ϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 4XJGU protocol EntityId { /* … */ }

    protocol Entity { var entityId: EntityId { get } } protocol UserId: EntityId { } typealias UserName = String protocol Profile: Entity { var userId: UserId { get } var name: UserName { get } var icon: URL? { get } } extension Profile { var entityId: EntityId { return userId } } (FOFSJD1SPUPDPM͸໘౗͕ଟ͍ͷͰ খࡉ޻Ͱར༻Λආ͚Δ
  23. ʮΞΧ΢ϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ ,PUMJO interface EntityId { /* ... */ }

    interface Entity<ID : EntityId> { val id: ID } interface UserId: EntityId typealias UserName = String interface Profile: Entity<UserId> { val name:UserName val icon: URI? }
  24. %%%తʹ͋΍͍͠Ϋϥεୡ΋ډΔ • ΫϥΠΞϯτଆʹ͍Δ • 8FC3UD˞ • 8FC3UD%FWJDF8FC3UD4JHOBMJOH8FC3UD4FUUJOHʜ • )UUQ-JWF4USFBNJOH˞ •

    "VEJP4PVSDF • 7JEFP4PVSDF • $BNFSB7JEFP4PVSDF4UJMM*NBHF7*EFP4PVSDF • %%%ͬΆ͘͸ͳ͍ʜ • ͔ͱ͍ͬͯݴ༿ͱͯ͠͸ઈର֎ͤͳ͍ʜ • Ͱ΋ศར ˞͜ΕΒ͸ࣗ෼ୡͷఆٛͨ͠΋ͷͰ ϥΠϒϥϦ౳ͷੜ0CKFDUΛࢦ͢΋ͷͰ͸͋Γ·ͤΜ
  25. উखʹ૿΍ͨ͠ఆٛୡ ΫϥΠΞϯτଆͰͷΈ%PNBJO૚ʹஔ͚ΔఆٛΛ૿΍͍ͯ͠Δ • -PDBM3FQPTJUPSZ • ϩʔΧϧͰ͔͠อଘ͠ͳ͍͔ΒͶʁ͍͍ͶʁΛڧௐͨ͠3FQPTJUPSZ • ओʹ$BDIF༻్Ͱ࢖͏ͷͰ$MFBS$MFBS"MMΛ͍࣋ͬͯΔ • %BUB4PVSDF

    • 3FQPTJUPSZͷѥछɻϩʔΧϧݶఆͷҰ࣌σʔλΛऔΓѻ͏ɻ • ߋ৽ݕग़ͷ EFMFHBUF  FWFOU-JTUFS ΛηοτͰ͖Δ • 7JFXʹηοτ͢Δ༻ • %BUB4UPSF • %BUB4PVSDFͷ্Ґ൛ɻ • σʔλͷ௥Ճߋ৽͕Ͱ͖Δɻ
  26. "QQMJDBUJPO૚ͷਂ۷Γ

  27. "QQMJDBUJPO૚ͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BS3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU

    #BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ͜ΕΛ ϝιου" ϝιου# ʜ
  28. "QQMJDBUJPO૚ͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BB3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU

    #BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ͜͏͢Δͱ ϝιου" ϝιου# ʜ
  29. "QQMJDBUJPO૚ͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BB3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU

    #BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ར༻Մೳʂ ར༻Մೳʂ ͜͏ͳΔʂ ϝιου" ϝιου# ʜ
  30. "QQMJDBUJPO૚ͷํ਑ • %*͞Εͨ%PNBJO૚ͷ࣮૷Λ૊Έ߹ΘͤͯԿ͔͠Βͷҙຯͷ͋ Δॲཧʹ͢Δ࣮૷Λஔ͘ • ଞͷ"QQMJDBUJPOΛड͚औΔࣄ΋͋ΔͷͰ࣮͸ଟ૚ • ෭࡞༻ͷ͋ΔॲཧΛ௚઀ߦ͏͜ͱ͸ͳ͘ɺ%*͞ΕͨΠϯελϯ ε΁ͷࢦࣔΛ௨ͯ͠ߦ͏ •

    λΠϚʔॲཧΛߦͬͨΓ"DUPSͰಈ͍ͯͨΓ͢Δ͜ͱ΋͋Γ݁ ߏࣗ༝
  31. ۩ମྫ r ܝࣔ൘ॻ͖ࠐΈΞϓϦ class CommentPoster(commentsRepository: CommentsRepository, commentFactory: CommentFactory){ def post(userId:UserId,

    body:String, timestamp:LocalDateTime): Future[Done] ={ val comment = commentFactory.build(userId, body, timestamp) commentsRepository.store(comment) } } ˞ͿͬͪΌ͚͜Ε͘Β͍ͷॲཧͳΒ"QQMJDBUJPOΛ࡞Βͣ $POUSPMMFSʹॻ͍ͯ͠·͏͜ͱ͕ଟ͍
  32. ۩ମྫ r $BDIF෇͖3FQPTJUPSZ protocol FooRepository { func get(id: FooId) ->

    Promise<Foo> func store(foo: Foo) -> Promise<Void> } protocol FooLocalRepository { func get(id: FooId) -> Promise<Foo> func store(foo: Foo) -> Promise<Void> func clear(id: FooId) func clearAll() } class CachedFooRepository: FooRepository {  ʜ  init(fooRepository: FooRepository, fooLocalRepository: FooLocalRepository) {  ʜ } func get(id: FooId) -> Promise<Foo> {  MPDBM3FQPTJUPSZ͔Βऔಘˠͳ͚Ε͹SFQPTJUPSZ͔Βऔಘ  } func store(foo: Foo) -> Promise<Void> {  SFQPTJUPSZʹอଘ͔ͯ͠Β MPDBM3FQPTJUPSZDMFBS   } } ˞1SPNJTFϥΠϒϥϦ͸ )ZESB Λ޷ΜͰ࢖͍ͬͯ·͢
  33. ۩ମྫ r $BDIF෇͖3FQPTJUPSZ interface FooRepository { suspend fun get(id: FooId):

    Foo suspend fun store(foo: Foo) } interface FooLocalRepository { suspend fun get(id: FooId): Foo suspend fun store(foo: Foo) fun clear(id:FooId) fun clearAll() } class CachedFooRepository( private val fooRepository: FooRepository, private val fooLocalRepository: FooLocalRepository ) : FooRepository { override suspend fun get(id: FooId): Foo { /* localRepositoryから取得→なければrepositoryから取得 */ } override suspend fun store(foo: Foo) { /* repositoryに保存してから localRepository.clear() */ } }
  34. ۩ମྫ r $BDIF$MFBOFS class CacheCleaner( private val fooLocalRepository: FooLocalRepository, private

    val barLocalRepository: BarLocalRepository, private val bazLocalRepository: BazLocalRepository ) { fun execute() { fooLocalRepository.clearAll() barLocalRepository.clearAll() bazLocalRepository.clearAll() } }
  35. ۩ମྫ r ϥΠϒ1MBZFSͷίΞ 1MBZFS6* EFMFHBUF ࠩ͠ࠐΈޱ Session ParticipantList DataStore ChatList

    DataStore ΍͍ͬͯΔۀ຿ • ঢ়گʹԠͯ͡)4-8FC35$ͷ઀ଓΛ࢖͍෼͚ΔΑ͏$POOFDUJPOʹࢦࣔΛग़͢ • 8FC4PDLFU&WFOU4PVSDF౳͔Βνϟοτ΍ࢀՃऀ৘ใΛड৴ͯ͠$IBU-JTU΍1BSUJDJQBOU-JTUʹ อଘ͢Δ • ϝιουΛ௨ͯ͠֎ଆʹ·ͱΊͨ৘ใΛฦͨ͠ΓɺࢦࣔΛड͚ͨΓ౳ Connection WebRtc Signaling Device WebSocket CommandFactory HLS EventSource (server sent event) QMBZFS4UBUVT DIBUૹ৴ DMPTF ʜ
  36. "EBQUFS૚ͷਂ۷Γ

  37. "EBQUFS૚ͷํ਑ • %PNBJO૚ͷఆٛ USBJUJOUFSGBDFQSPUPDPM Λܧঝ࣮ͯ͠૷͠ ͨΫϥεͷஔ͖৔ • &OUJUZ7BMVF 0CKFDUͰ͋Ε͹ DBTFDMBTTEBUBDMBTT

    TUSVDUͰ࡞ͬͨΓ౳ • +40/౳ͱͷ૬ޓม׵΋͜͜Ͱॻ͍ͯ͠·͏ • 3FQPTJUPSZͰ͋Ε͹%#΍"1*ͱ௨৴ͨ͠Γςετ༻ʹΦϯϝ ϞϦͷ࣮૷Λ࡞ͬͨΓɻ ͱ͘ʹ޻෉͸ͳ͍ͷͰ۩ମྫ͸লུ͠·͢
  38. 7JFX૚ͷਂ۷Γ

  39. 7JFX NJTD ૚ͷํ਑ • "QQMJDBUJPOΛΠϯελϯεԽͯ͠ը໘Λग़ྗߏங͢Δ • αʔόଆ͸+40/ు͚ͩ͘ͳͷͰγϯϓϧ • ΫϥΠΞϯτଆ͸ը໘ͷෳࡶ͞ʹԠͯ͡࿈ܞํ๏Λม͍͑ͯΔ •

    ओͳॅਓ • 6*7JFX$POUSPMMFS6*7JFX"DUJWJUZ'SBHNFOU QMBZͷ $POUSPMMFS ͳͲͷίϯτϩʔϥʔܥ • 4UPSZCPBSE9JCMBZPVUͷ9.- σΟϨΫτϦతʹ͸ผʣ "1*ͷҾ਺ ΍ฦ஋ͷ+40/ૢ࡞ • %*ίϯςφ΋͜ͷ૚
  40. αʔόଆͷ7JFX࣮૷ • جຊ3&45 "1*ܗࣜͰ+40/ฦ͚ͩ͢ • &OUJUZ7BMVF0CKFDUͱ+40/ͷ૬ޓม׵͸"EBQUFS૚Ͱ࣮૷͍ͯ͠Δ͜ͱ΋͋Δ͕ɺ ग़ྗͷ౎߹ʹҾͬுΒΕͨ͘ͳ͍ͷͰɺ͜ΕΒΛૉͷ··ฦ͢͜ͱ͸গͳ͍ • جຊ"1*ຖʹฦ౴ܗࣜΛܾΊͯͦͪΒʹ٧Ίସ͑Δ import

    play.api.libs.json.Json case class FooGetApiResponse(id: FooId, description: String) object FooGetApiResponse { implicit val jsonWrites = Json.writes[FooResponse] }
  41. J04ͷ7JFX࣮૷  ௒γϯϓϧͳը໘ • ௒؆୯ͳը໘Ͱ͋ΔͳΒͦͷ··7JFX$POUSPMMFSͱ4UPSZCPBSE͚ͬͭ͘Δ • 4UPSZCPBSE͸جຊը໘ຖʹ࡞੒ • Ұ࿈ͷը໘ʢΞϓϦىಈޙͷ͍Ζ͍Ζ঺հϑϩʔͳͲʣ͸ෳ਺ը໘Λ֨ೲ 閉じるボタン

    class FooViewController: UIViewController { @IBAction func onTouchUpInsideCloseBotton(_: Any) { navigationController?.popViewController(animated: true) } } 4UPSZCPBSE
  42. J04ͷ7JFX࣮૷  ΍΍ෳࡶͳը໘ • ଟগෳࡶͳΒ7JFXૢ࡞͸ΧελϜ7JFXʹԡ͠ࠐΊͯ͠·͏ • 7JFX$POUSPMMFS͸ΧελϜ7JFX΁ͷσʔληοτ SFQPTJUPSZΛݺͼग़͢౳ ͱɺ 7JFX͔Βͷૢ࡞΁ͷରԠ͕ओͳ࢓ࣄɻ

    Submit TextText TextTextTextTextText TextTextText TextText TextTextText 9JC ੜૢ࡞ !*#"DUJPO protocol FooViewDelegate: class { func onSubmit(value: String) } class FooView: UIView { /* ... */ weak var delegate: FooViewDelegate? var foo: Foo? { didSet { setNeedsLayout() } } override func layoutSubviews() { super.layoutSubviews() /* fooを描画 */ } } 'PP7JFX class FooViewController: UIViewController { @IBOutlet weak var fooView: FooView? { didSet { fooView?.delegate = self } } /* ... */ } extension FooViewController: FooViewDelegate { func onSubmit(value: String) { /* … */ } } 'PP7JFX$POUSPMMFS ᖤա͞Εͨ ૢ࡞ EFMFHBUF σʔλ ࢦࣔ QSPQFSUZTFU NFUIPEDBMM ඳը QSPQFSUZTFU
  43. J04ͷ7JFX࣮૷  ௒ෳࡶͳը໘ • 7JFX$POUSPMMFSͷ࢓ࣄ͕ଟ͍ͳΒ"QQMJDBUJPO૚ͷΫϥεʹ೚ ͤͯ͠·͏ Submit TextText TextTextTextTextText TextTextText

    TextText TextTextText 9JC ඳը QSPQFSUZTFU ੜૢ࡞ !*#"DUJPO σʔλ ࢦࣔ QSPQFSUZTFU NFUIPEDBMM ᖤա͞Εͨ ૢ࡞ EFMFHBUF class FooView: UIView { /* ... */ } 'PP7JFX class FooViewController : UIViewController, PlayerUI { /* ... */ } 'PP7JFX$POUSPMMFS 1MBZFS6* ΋ͬͱ ᖤա͞Εͨ ૢ࡞ NFUIPEDBMM Πϕϯτ EFMFHBUF 1MBZFS$PSF
  44. "OESPJEͷ7JFX࣮૷  ௒γϯϓϧͳը໘ • ௒؆୯ͳը໘Ͱ͋ΔͳΒ,PUMJO"OESPJE&YUFOTJPOTͰ"DUJWJUZ ͔Β6*ύʔπΛͦͷ··ૢ࡞ YNM 閉じるボタン import kotlinx.android.synthetic.main.fooview.*

    class FooActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.fooview) buttonView.setOnClickListener { /* … */ } } }
  45. "OESPJEͷ7JFX࣮૷  ΍΍ෳࡶͳը໘ • ෳࡶͳը໘ͳΒͰ"OESPJE"SDIJUFDUVSF$PNQPOFOUTͷ 7JFX.PEFM-JWF%BUBͱ%BUB#JOEJOHΛ׆༻ • J04ͱൺֱ͢Δͱɺߏ଄͸ಉ͡Ͱ΋ΧελϜ7JFXΛ࡞Βͳͯ͘ྑ͍৔໘͕ ຆͲʹͳΔΠϝʔδ YNM

    Submit TextText TextTextTextTextText TextTextText TextText TextTextText class FooViewModel : ViewModel() { interface Listener { fun onSubmit() } var listener: Listener? by weakVar() val foo = MutableLiveData<Foo>() fun onClickSubmitButton() { listener?.onSubmit() } } ૒ํ޲ #JOEJOH class FooActivity : FragmentActivity() { override fun onCreate( savedInstanceState: Bundle?) { /* BindingとViewModelの⽤意 */ model.listener = this } } } σʔλ QSPQFSUZTFU ᖤա͞Εͨ ૢ࡞ EFMFHBUF
  46. "OESPJEͷ7JFX࣮૷  ௒ෳࡶͳը໘ • "DUJWJUZ'SBHNFOUͷ࢓ࣄ͕ଟ͍ͳΒ"QQMJDBUJPO૚ͷΫϥεʹ ೚ͤͯ͠·͏ Submit TextText TextTextTextTextText TextTextText

    TextText TextTextText 9JC σʔλ ࢦࣔ QSPQFSUZTFU NFUIPEDBMM ᖤա͞Εͨ ૢ࡞ EFMFHBUF class FooViewModel: ViewModel() { /* ... */ } 'PP7JFX.PEFM 'PP"DUJWJUZ 1MBZFS6* ΋ͬͱ ᖤա͞Εͨ ૢ࡞ NFUIPEDBMM Πϕϯτ EFMFHBUF 1MBZFS$PSF class FooActivity : FragmentActivity(), PlayerUI { override fun onCreate() { /* … */ } } ૒ํ޲ #JOEJOH
  47. ঺հ͸Ҏ্Ͱ͢ ೗ԿͰͨ͠Ͱ͠ΐ͏͔ʁ

  48. 8FBSFIJSJOH IUUQTTVHBSDPSQKQ

  49. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ