Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
SUGARのアーキテクチャー / SUGAR ARCHITECTURE 20190425
Search
sugitani
April 25, 2019
Technology
2
2.1k
SUGARのアーキテクチャー / SUGAR ARCHITECTURE 20190425
2019/4/25に行われた「突撃!!隣のアーキテクチャ」
https://totsugeki-architecture.connpass.com/event/125845/
での発表資料です
sugitani
April 25, 2019
Tweet
Share
Other Decks in Technology
See All in Technology
Zero Trust DNS でより安全なインターネット アクセス
murachiakira
0
140
GCASアップデート(202508-202510)
techniczna
0
240
次世代のメールプロトコルの斜め読み
hirachan
3
260
オブザーバビリティが育むシステム理解と好奇心
maruloop
3
1.9k
251029 JAWS-UG AI/ML 退屈なことはQDevにやらせよう
otakensh
0
120
AIでデータ活用を加速させる取り組み / Leveraging AI to accelerate data utilization
okiyuki99
6
1.6k
OTEPsで知るOpenTelemetryの未来 / Observability Conference Tokyo 2025
arthur1
0
410
触れるけど壊れないWordPressの作り方
masakawai
0
620
kotlin-lsp の開発開始に触発されて、Emacs で Kotlin 開発に挑戦した記録 / kotlin‑lsp as a Catalyst: My Journey to Kotlin Development in Emacs
nabeo
2
210
ざっくり学ぶ 『エンジニアリングリーダー 技術組織を育てるリーダーシップと セルフマネジメント』 / 50 minute Engineering Leader
iwashi86
8
4.2k
20251027_マルチエージェントとは
almondo_event
1
510
NOT A HOTEL SOFTWARE DECK (2025/11/04)
notahotel
0
800
Featured
See All Featured
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
231
22k
A Modern Web Designer's Workflow
chriscoyier
697
190k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
24k
Why You Should Never Use an ORM
jnunemaker
PRO
60
9.6k
How STYLIGHT went responsive
nonsquared
100
5.9k
Facilitating Awesome Meetings
lara
57
6.6k
Large-scale JavaScript Application Architecture
addyosmani
514
110k
Visualization
eitanlees
150
16k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
253
22k
Side Projects
sachag
455
43k
Making Projects Easy
brettharned
120
6.4k
Transcript
46("3ͷΞʔΩςΫνϟ ಥܸʂʂྡͷΞʔΩςΫνϟ !TVHJUBOJ " 3 $ ) * 5 &
$ 5 6 3 &
લઆ
ࣗݾհ • ਿ୩ͱਃ͠·͢ • ܦྺ • υϫϯΰ χίχίੜ์ૹ • ηϓςʔχɾΦϦδφϧ
$50 ("/." • 46("3$50 46("3 !TVHJUBOJ
46("3ͱʁ • ஶ໊ਓʹಛԽͨ͠ϥΠϒ৴Ξ ϓϦ • ొͯ͠ΞʔςΟετΛϑΥϩʔ͠ ͓ͯ͘ͱి $BMM,JU ͕དྷΔ •
Ұఆ͕ϐοΫΞοϓ͞ΕબΤϦ ΞʹೖΔʢ௨শ͓ण࢘Ϩʔϯʣ • ΞʔςΟετ͕௨ରΛબͿͱ௨ ͕Ͱ͖Δ ଞͷਓͦͷ༷ࢠΛ ோΊΔ • ৴ऀ͕εΰ͘ͳ͚ΕΓཱ ͨͳ͍ • ৴ऀ͕Ϡό͍
ΞʔΩςΫνϟհ͡Ί·͢ ˞͝ҙ˞ %%%ͳͲ͍ΖΜͳ୯ޠ͕Ͱ͖ͯ·͕͢ ຊϓϨθϯςʔγϣϯฐࣾͷݱঢ়ͷհʹա͗ͣ Կ͔͠Βͷํ๏ͷਖ਼͍࣮͠ફํ๏Λࣔ͢ͷͰ͋Γ·ͤΜ
࠾༻ΞʔΩςΫνϟͱհൣғ 4DBMBαʔό 4XJGUJ04 ,PUMJO"OESPJE ͥΜͿΔ γεςϜΞʔΩςΫνϟ %*1Λ׆༻͢ΔҰʁ γεςϜΞʔΩςΫνϟ %*1Λ׆༻͢ΔҰʁ γεςϜΞʔΩςΫνϟ
%*1Λ׆༻͢ΔҰʁ (6*ΞʔΩςΫνϟ ແ͠ +40/ग़͚ͩ͢ (6*ΞʔΩςΫνϟ .7$͔ͳ͊ʜʁ (6*ΞʔΩςΫνϟ .77.ͩͱࢥͬͯΔ
ͲΕجຊతʹߏҰॹ TSD EPNBJO BQQMJDBUJPO BEBQUFS WJFX *OGSBTUSVDUVSF
Πϝʔδ͓ͳ͡Έͷԁܗ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF
%PNBJO %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF %%%Ͱੜ͞ΕΔ֓೦ͷॅ·͏ॴ &OUJUZ7BMVF0CKFDU4FSWJDF 3FQPTJUPSZͷఆٛ
USBJUJOUFSGBDFQSPUPDPM Λஔ͘
"QQMJDBUJPO %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF υϝΠϯͷొਓΛ ҙຯͷ͋Δॲཧʹ·ͱΊΔΫϥεͷॅ·͏ॴ *OKFDU͞Εͨ%PNBJOΠϯελϯεΛͬͯͻͱ·ͱ·Γ
ͷॲཧʢྫσʔλΛ3FQPTJUPSZ͔ΒݺΜͰ4FSWJDFʹ௨ ͢ͳͲՃ͔ͯ͠Βอଘ͢Δ Λߦ͏࣮͔ఆٛΛஔ͘
"EBQUFS %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF υϝΠϯͷ࣮ͷॅ·͏ॴ υϝΠϯͷఆٛΛܧঝ࣮ͨ͠ʢ%#ʹ ΞΫηε͢Δ3FQPTJUPSZʣΛஔ͘
7JFX ͦͷଞʣ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF 6*ͱίϯτϩʔϥ 6*7JFX$POUSPMMFS6*7JFX"DUJWJUZ'SB
HNFOU QMBZͷ $POUSPMMFSͳͲݟͨ ͱૢ࡞ͷ࢝·Γ͔ΒऴΘΓ·Ͱͷ੍ޚʹ ؔΘΔ࣮Λஔ͖·͢ɻ
*OGSBTUSVDUVSF %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF ൚༻తͳͷஔ͖ จࣈྻ࣌ࠁૢ࡞ϢʔςΟϦςΟɺ"84 ͷԿ͔͠ΒΛ͍͘͢ϥοϓͨ͠ͷ
ͷͲ͜Ͱ͑ͦ͏ͳ࣮Λஔ͘ɻ
ଆ͚ͩར༻Ͱ͖Δ %PNBJO "QQMJDBUJPO "EBQUFS 7JFX ͦͷଞ *OGSBTUSVDUVSF ґଘੑٯసͷݪଇΛ׆༻ %PNBJOr 7JFXɺ"EBQUFSɺ"QQMJDBUJPOΛར༻
JNQPSU Ͱ͖ͳ͍ "QQMJDBUJPOr %PNBJOΛར༻Ͱ͖Δɻ 7JFXɺ"EBQUFSΛར༻Ͱ͖ͳ͍ "EBQUFSr"QQMJDBUJPOɺ%PNBJOΛར༻Ͱ͖Δɻ7JFXΛར༻Ͱ͖ͳ͍ 7JFXr "EBQUFSɺ"QQMJDBUJPOɺ%PNBJOΛར༻Ͱ͖Δ *OGSBTUSVDUVSFr ଞΛར༻Ͱ͖ͳ͍ɻଞ͔Βར༻͞ΕΔ
%PNBJOͷਂ۷Γ
%PNBJOͷํ • υϝΠϯۦಈઃܭͰϞσϦϯάΛߦ͏ • &SJDຊʹͰ͖Δ͚ͩ४ڌͯ͠ݕ౼͢Δ • ʹग़ͯٞ͢͠Δ • ݪଇఆٛ USBJUJOUFSGBDFQSPUPDPM
Λஔ͘ • γϯϓϧͳ7BMVF0CKFDUʹྫ֎͋Δ • طͷΫϥε63-ΫϥεΛ͔ͭͬͨΓ • UZQFBMJBT'PP4USJOH TXJGULPUMJO "OZ7BM TDBMB Ͱදݱͨ͠Γ • &OVNͬͨΓ • ಉ͡ଘࡏʹରͯ͠αʔόͱΫϥΠΞϯτͰಉ͡ݴ༿ݟ͑ํʹ ͳΔͱݶΒͳ͍͜ͱʹؾΛ͚ͭΔ
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 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 }
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 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 } ొࡁΈͰͳ͍Ϣʔβʔ͍ͩͬͯΔͷҙ
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
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 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 } }
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 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 } } ΫϥΠΞϯτ͔Β͢Εਓ͡Όͳͯ͘ ϓϩϑΟʔϧ͔͠ڵຯͳ͍ΑͶɺ ͷҙΛࠐΊ໋໊ͨ
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ 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໘͕ଟ͍ͷͰ খࡉͰར༻Λආ͚Δ
ʮΞΧϯτΛ࣋ͬͨར༻ऀʯͷ ఆٛΠϝʔδ ,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? }
%%%తʹ͍͋͠ΫϥεୡډΔ • ΫϥΠΞϯτଆʹ͍Δ • 8FC3UD˞ • 8FC3UD%FWJDF8FC3UD4JHOBMJOH8FC3UD4FUUJOHʜ • )UUQ-JWF4USFBNJOH˞ •
"VEJP4PVSDF • 7JEFP4PVSDF • $BNFSB7JEFP4PVSDF4UJMM*NBHF7*EFP4PVSDF • %%%ͬΆ͘ͳ͍ʜ • ͔ͱ͍ͬͯݴ༿ͱͯ͠ઈର֎ͤͳ͍ʜ • Ͱศར ˞͜ΕΒࣗୡͷఆٛͨ͠ͷͰ ϥΠϒϥϦͷੜ0CKFDUΛࢦ͢ͷͰ͋Γ·ͤΜ
উखʹ૿ͨ͠ఆٛୡ ΫϥΠΞϯτଆͰͷΈ%PNBJOʹஔ͚ΔఆٛΛ૿͍ͯ͠Δ • -PDBM3FQPTJUPSZ • ϩʔΧϧͰ͔͠อଘ͠ͳ͍͔ΒͶʁ͍͍ͶʁΛڧௐͨ͠3FQPTJUPSZ • ओʹ$BDIF༻్Ͱ͏ͷͰ$MFBS$MFBS"MMΛ͍࣋ͬͯΔ • %BUB4PVSDF
• 3FQPTJUPSZͷѥछɻϩʔΧϧݶఆͷҰ࣌σʔλΛऔΓѻ͏ɻ • ߋ৽ݕग़ͷ EFMFHBUF FWFOU-JTUFS ΛηοτͰ͖Δ • 7JFXʹηοτ͢Δ༻ • %BUB4UPSF • %BUB4PVSDFͷ্Ґ൛ɻ • σʔλͷՃߋ৽͕Ͱ͖Δɻ
"QQMJDBUJPOͷਂ۷Γ
"QQMJDBUJPOͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BS3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU
#BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ͜ΕΛ ϝιου" ϝιου# ʜ
"QQMJDBUJPOͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BB3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU
#BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ͜͏͢Δͱ ϝιου" ϝιου# ʜ
"QQMJDBUJPOͷΠϝʔδ 'PP3FQPTJUPSZ ࠩ͠ࠐΈޱ #BB3FQPTJUPSZ ࠩ͠ࠐΈޱ #B['BDUPSZ ࠩ͠ࠐΈޱ "4QFDJBM"QQMJDBUJPO 'PP3FQPTJUPSZ *NQMFNFOU
#BS3FQPTJUPSZ *NQMFNFOU #B['BDUPSZ *NQMFNFOU ར༻Մೳʂ ར༻Մೳʂ ͜͏ͳΔʂ ϝιου" ϝιου# ʜ
"QQMJDBUJPOͷํ • %*͞Εͨ%PNBJOͷ࣮ΛΈ߹ΘͤͯԿ͔͠Βͷҙຯͷ͋ Δॲཧʹ͢Δ࣮Λஔ͘ • ଞͷ"QQMJDBUJPOΛड͚औΔࣄ͋ΔͷͰ࣮ଟ • ෭࡞༻ͷ͋ΔॲཧΛߦ͏͜ͱͳ͘ɺ%*͞ΕͨΠϯελϯ εͷࢦࣔΛ௨ͯ͠ߦ͏ •
λΠϚʔॲཧΛߦͬͨΓ"DUPSͰಈ͍ͯͨΓ͢Δ͜ͱ͋Γ݁ ߏࣗ༝
۩ମྫ 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ʹॻ͍ͯ͠·͏͜ͱ͕ଟ͍
۩ମྫ 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 ΛΜͰ͍ͬͯ·͢
۩ମྫ 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() */ } }
۩ମྫ 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() } }
۩ମྫ r ϥΠϒ1MBZFSͷίΞ 1MBZFS6* EFMFHBUF ࠩ͠ࠐΈޱ Session ParticipantList DataStore ChatList
DataStore ͍ͬͯΔۀ • ঢ়گʹԠͯ͡)4-8FC35$ͷଓΛ͍͚ΔΑ͏$POOFDUJPOʹࢦࣔΛग़͢ • 8FC4PDLFU&WFOU4PVSDF͔ΒνϟοτࢀՃऀใΛड৴ͯ͠$IBU-JTU1BSUJDJQBOU-JTUʹ อଘ͢Δ • ϝιουΛ௨ͯ͠֎ଆʹ·ͱΊͨใΛฦͨ͠ΓɺࢦࣔΛड͚ͨΓ Connection WebRtc Signaling Device WebSocket CommandFactory HLS EventSource (server sent event) QMBZFS4UBUVT DIBUૹ৴ DMPTF ʜ
"EBQUFSͷਂ۷Γ
"EBQUFSͷํ • %PNBJOͷఆٛ USBJUJOUFSGBDFQSPUPDPM Λܧঝ࣮ͯ͠͠ ͨΫϥεͷஔ͖ • &OUJUZ7BMVF 0CKFDUͰ͋Ε DBTFDMBTTEBUBDMBTT
TUSVDUͰ࡞ͬͨΓ • +40/ͱͷ૬ޓม͜͜Ͱॻ͍ͯ͠·͏ • 3FQPTJUPSZͰ͋Ε%#"1*ͱ௨৴ͨ͠Γςετ༻ʹΦϯϝ ϞϦͷ࣮Λ࡞ͬͨΓɻ ͱ͘ʹͳ͍ͷͰ۩ମྫলུ͠·͢
7JFXͷਂ۷Γ
7JFX NJTD ͷํ • "QQMJDBUJPOΛΠϯελϯεԽͯ͠ը໘Λग़ྗߏங͢Δ • αʔόଆ+40/ు͚ͩ͘ͳͷͰγϯϓϧ • ΫϥΠΞϯτଆը໘ͷෳࡶ͞ʹԠͯ͡࿈ܞํ๏Λม͍͑ͯΔ •
ओͳॅਓ • 6*7JFX$POUSPMMFS6*7JFX"DUJWJUZ'SBHNFOU QMBZͷ $POUSPMMFS ͳͲͷίϯτϩʔϥʔܥ • 4UPSZCPBSE9JCMBZPVUͷ9.- σΟϨΫτϦతʹผʣ "1*ͷҾ ฦͷ+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] }
J04ͷ7JFX࣮ γϯϓϧͳը໘ • ؆୯ͳը໘Ͱ͋ΔͳΒͦͷ··7JFX$POUSPMMFSͱ4UPSZCPBSE͚ͬͭ͘Δ • 4UPSZCPBSEجຊը໘ຖʹ࡞ • Ұ࿈ͷը໘ʢΞϓϦىಈޙͷ͍Ζ͍ΖհϑϩʔͳͲʣෳը໘Λ֨ೲ 閉じるボタン
class FooViewController: UIViewController { @IBAction func onTouchUpInsideCloseBotton(_: Any) { navigationController?.popViewController(animated: true) } } 4UPSZCPBSE
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
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
"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 { /* … */ } } }
"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
"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
հҎ্Ͱ͢ ԿͰͨ͠Ͱ͠ΐ͏͔ʁ
8FBSFIJSJOH IUUQTTVHBSDPSQKQ
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ