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
2k
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
dbt開発 with Claude Codeのためのガードレール設計
10xinc
2
1.2k
OCI Oracle Database Services新機能アップデート(2025/06-2025/08)
oracle4engineer
PRO
0
120
Platform開発が先行する Platform Engineeringの違和感
kintotechdev
4
560
ブロックテーマ時代における、テーマの CSS について考える Toro_Unit / 2025.09.13 @ Shinshu WordPress Meetup
torounit
0
120
2025年夏 コーディングエージェントを統べる者
nwiizo
0
140
「Linux」という言葉が指すもの
sat
PRO
4
130
RSCの時代にReactとフレームワークの境界を探る
uhyo
10
3.4k
テストを軸にした生き残り術
kworkdev
PRO
0
200
共有と分離 - Compose Multiplatform "本番導入" の設計指針
error96num
2
400
下手な強制、ダメ!絶対! 「ガードレール」を「檻」にさせない"ガバナンス"の取り方とは?
tsukaman
2
440
5年目から始める Vue3 サイト改善 #frontendo
tacck
PRO
3
220
実践!カスタムインストラクション&スラッシュコマンド
puku0x
0
380
Featured
See All Featured
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
188
55k
Practical Orchestrator
shlominoach
190
11k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
8
920
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Navigating Team Friction
lara
189
15k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
48
9.7k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
8
520
GraphQLの誤解/rethinking-graphql
sonatard
72
11k
GraphQLとの向き合い方2022年版
quramy
49
14k
It's Worth the Effort
3n
187
28k
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
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ