èªãã æ¬
èè
: Steven van Deursen, Mark Seemann
èš³è
: é ç°æºä¹
è¡šçŽã«ã¯.NETãC#ã®æåã¯ãªãã®ã§ãããåã®çã¯"Dependency Injection in .NET"ã§.NETãåæããæ¬ã®ããã§ããã
ãã ãã¯ããã«ã§
æ¬æžã§ã¯ã.NETãšC#ãçšããŠãäŸåæ³šå ¥ã«é¢ããçšèªãæéãå æ¬çã«çŽ¹ä»ããæåããŠããã®ã§ãããæ¬æžã®äŸ¡å€ã.NETã®å€ã®äžçã«ãå±ãããšãæãã§ããŸãã
ãšãããŸããã
Rustã®DIã§ãªã«ã掻ãããæããæåŸ
ããŠãèªãã§ã¿ãŸããã
第1éš äŸåæ³šå ¥ (Dependency Injection: DI) ã®åœ¹å²
第1ç« äŸåæ³šå ¥ (Dependency Injection: DI) ã®åºæ¬: äŸåæ³šå ¥ãšã¯äœãªã®ã? ãªã䜿ãã®ã? ã©ã®ããã«äœ¿ãã®ã?
ãŸããä¿å®å®¹ææ§(maintainability)ãé«ãããšããç®çãããã
ãã®ããã«ãççµå(loose couplig)ãªèšèšãå¿
èŠã
äŸå泚å
¥ã¯ãççµåãªã³ãŒããå®çŸããããã®ãã¯ããã¯ã®ã²ãšã€ã
ççµåã ãšã¡ã³ããã³ã¹ãããããªãã®ã¯ã責åãæ確ã«ãªããåäœãã¹ããè¡ãããããæ¡åŒµå®¹ææ§ãåäžããããã
ãããããªãã§ãæœè±¡åããŠæ³šå ¥ããã°ããããã§ã¯ãªããäŸåã«ã¯ãå®å®äŸå(stable dependency)ãšæ®çºæ§äŸå(volatile dependency)ãããã
æ®çºæ§äŸåãšã¯ãå€éšã®DBã®ããã«åããã«ã¯å¥ã®èšå®ãå¿ èŠã«ãªããã®ããé決å®ç(ã©ã³ãã ãçŸåšæå»)ãªãã®ãããã
äŸåæ³šå ¥ããããšããããç¹ã¯ãäŸåãå©çšããåŽãããäŸåã®çæãå¶åŸ¡ã®è²¬åãåãé€ãããäŸåãwrapããŠãåŠçãè¿œå ãããšãã£ãããšãå¯èœã«ãªãããã
第2ç« å¯çµåããã³ãŒãã§æ§ç¯ãããã¢ããªã±ãŒã·ã§ã³
æ¬ç« ã§ã¯ãUIããã¡ã€ã³ãããŒã¿ã¢ã¯ã»ã¹ã®3å±€æ§é ã®ã¢ããªã±ãŒã·ã§ã³ãå¯çµåãããšå®éã«ã©ããªã³ãŒãã«ãªãã®ããå ·äœçã«çŽ¹ä»ãããŸãã
åäžè²¬ä»»ã®åå(Single Responsibility Principal)ã®èª¬æãã§ãŠããŸããŠãããããã¯ã©ã¹ã®å€æŽçç±ã¯äžã€ã§ããã¹ããããããå€æŽã®çç±ãäžã€ãã®å€æã¯é£ãããããã§èŠ³ç¹ãšããŠãåéæ§(cohesion)ã«çç®ãããšè¯ããåéæ§ãšã¯ãèŠçŽ å士ã®æ©èœçãªé¢é£åºŠã®ããšã
泚æãå¿
èŠãªã®ã¯ãåäžè²¬ä»»ã®ååã劥åœããªãå Žåãããããã®éã«ç¡çãããæ¬ååãé©çšããŠããŸããšãäžèŠãªåå²ãšãªãè€éãããããŠããŸãçµæã«ãªãããšãããããã
ãšããããšã§ã倧äºãªã®ã¯ãããŸã§ãä¿å®ãããããã©ããã§ãããšãããŠããŸããã
æå€ãšåäžè²¬ä»»ã®ååã劥åœããªãå ŽåããããšæèšããŠããã®ã¯çãããšæããŸããã(ããååãåžžã«ããŠã¯ãŸãäŸã¯çšãªã®ã§ãåæãšãªã£ãŠããã ããããããŸããã)
ç§ã¯ãåäžè²¬ä»»ãäžã€ã®è²¬åããããããšããèãæ¹ã«ã¯å€§è³æãªã®ã§ããããã®"äžã€"ã¯ã解é次第ã§æºããã¡ã ãªãšãã€ãæããŸããã®ã§ãæè¿ã¯ãã¹ãããããããã°OKãšããŠããã¹ãã®ããããè³äžäž»çŸ©ã«åŸããŠããŸãã(è€æ°ã®è²¬åããã°èªç¶ãšãã¹ãã®æºåãassertionæžãã¥ãããªããŸãã)
第3ç« ççµåãªã³ãŒããžã®å€æ
åç« ã§æ±ã£ãã¢ããªã±ãŒã·ã§ã³ãäŸåã泚å
¥ãã圢ã§ççµã«ããŠãããŸãã
äŸåãã³ã³ã¹ãã©ã¯ã¿çµç±ã§æž¡ããšã以äžã®ããã«äŸåã®äŸåãäœãæ§ãªã³ãŒãã«ãªããŸãããã
new
ããã¯ãäŸåã®å¶åŸ¡ã®è² æ
ãå¥ã®å Žæã«æŒãä»ããŠããã ãã§ã¯ãšããåã«å¯ŸããŠããã®è² æ
ãå¥ã®ãšããã«ç§»ãããšããã®ããéèŠãªã®ã ãšèª¬æãããŠããŸããã
ãããŠãäŸåã®æ³šå
¥ãäžäœã«ç§»ããŠãã£ãçµæãã¢ããªã±ãŒã·ã§ã³ã®æäžäœã«ããåæèµ·ç¹(Composition Root)ã§äŸå泚å
¥ãè¡ããããã«ãªããŸãã
äŸå泚å
¥ãè¡ããšãäœãã©ããåŒã³åºããŠããããããã«ææ¡ã§ããªããšããåé¡ç¹ããããŸãã
ããããåæèµ·ç¹ã§äŸå泚å
¥ãè¡ãããšã§ãåé床ã®é«ãç¶æ
ã§ããªããžã§ã¯ãã®äŸåé¢ä¿ãææ¡ã§ãããšãããŠããŸãã
äŸåæ§é転ã®åå(Dependency Inversion Principle)ã®èª¬æãããã®ã§ãã æœè±¡ã¯ãã®æœè±¡ã䜿ãã¢ãžã¥ãŒã«ã«ãã£ãŠææãããã¹ãã§ãæœè±¡ãå©çšããã¢ãžã¥ãŒã«ããã®æœè±¡ããã£ãšãæå¹æŽ»çšã§ããæ¹æ³ã§å®çŸ©ã§ããããã«ããªããã°ãªããªããšãã説æãããããããã£ãã§ãã
DDDã®æèã§ã¯ããã¡ã€ã³ãšã€ã³ãã©å±€ã®å±€éã®å®å®æ§ã®éããããã€ã³ã¿ãŒãã§ãŒã¹ããã¡ã€ã³åŽã«å®çŸ©ãããšãã£ãã®ã§ãããããŸãçŽåŸã§ããŠããªãã£ãã®ã§ããã¡ãã®èª¬æã®ã»ãã奜ã¿ã§ããã
第2éš ã«ã¿ãã°
第4ç« äŸåæ³šå ¥ã®ãã¿ãŒã³
å®éã«äŸåãæ³šå ¥ããããããã®èšèšãã¿ãŒã³ã«ã€ããŠã
åæåºç¹ (Composition Root)
DIã¯ã¢ããªã±ãŒã·ã§ã³ã®ãšã³ããªãŒãã€ã³ãã«å¯èœãªéãè¿ããšããã§è¡ãã
倧åãªã®ã¯ãäŸåé¢ä¿ãã©ã®ããã«æ§ç¯ããããã«ã€ããŠãcomposition rootããããç¥ãå¯äžã®å Žã«ãªãããšã
äŸåé¢ä¿ã®è§£æ±ºãäžç®æã§è¡ããšæããšãå¿
ç¶çã«mainã«è¿ããšããã§äŸåã®è§£æ±ºãè¡ãããããã«ãªãã
äŸåã®æ³šå
¥ããšã³ããªãŒãã€ã³ãã§è¡ããšããšã³ããªãŒãã€ã³ãã®äŸåãå¢ããŠããŸããšããæžå¿µãããã
ããããããã¯æšç§»çãªäŸå(A -> B -> Cã®ãšããA -> Cã®äŸå)ãèæ
®ããŠããªãããã§ããããèæ
®ã«ããããšããšã³ããªãŒãã€ã³ãã§äŸåã解決ããã»ãããå
šäœã®äŸåã®æ°ãæžãããã
æšç§»çãªäŸåããäŸåã®æ°ãšããŠèããããšããªãã£ãã®ã§ããã®èãã¯æ°ããã£ãã
ã³ã³ã¹ãã©ã¯ã¿çµç±ã§ã®æ³šå ¥ (Constructor Injection)
äŸåãconstructæã«æž¡ãæ¹åŒãåºæ¬çã«ã¯ãã®æ¹åŒãæšå¥šã§ãconstructæã«äŸåãæž¡ããªããããªå Žåã«ã¡ãœããçµç±ã§ã®æ³šå ¥ãæ€èšããã
ã¡ãœããçµç±ã§ã®æ³šå ¥ (Method Injection)
Composition rootã§ã¯ãäŸåãå©çšããåŽããŸã ãååšããŠããªãã£ããããªã¯ãšã¹ãæã®æ
å ±çã泚å
¥ããéã¯ãã¡ãœããçµç±ã§äŸåãæž¡ãã
éã«èšããšãconstructæã«æž¡ããäŸåã¯ã¡ãœããçµç±ã§æž¡ããªãããã«ããã»ããããã
ããããã£çµç±ã§ã®äŸå (Property Injection)
äŸåãpropertyã«èšå®ããããšã§æ³šå ¥ããæ¹åŒã
let mut foo = new;
foo.dependency = new;
foo.do_something;
éåžžã¯ããã©ã«ãã®äŸåãå©çšããããå©çšåŽãç¹å¥ãªããšããããå Žåã¯ãäŸåãäžæžãã§ããããã«ããŠããããã±ãŒã¹ã§çšããããã
ããŸãå©çšãããšãã©ã€ãã©ãªã®APIãã·ã³ãã«ã«ã§ããã
äžæ¹ã§ãç¹å®ã®äŸåãpropertyã«æ³šå
¥ãããããšãæé»çã«æåŸ
ãããããªã³ãŒãã ãšãã³ãŒãã®å«ãªèã(code smell)ã«ã€ãªããã®ã§æ³šæãå¿
èŠã
第5ç« äŸåæ³šå ¥ã®ã¢ã³ãã»ãã¿ãŒã³
äŸåæ³šå ¥ã«ãŸã€ããã¢ã³ããã¿ãŒã³ã«ã€ããŠã
ã³ã³ãããŒã«ã»ããªãŒã¯ (Control Freak)
Composition root以å€ã®ãšããã§ãæ®çºæ§äŸåãçæããŠãä¿æããããšã
ã®ããã«ãDIã§äŸåãããããã«èªèº«ã§çæããŠããŸãããšã
ãããããããããããªãããã«DIãããã°ã£ãŠããã
ãµãŒãã¹ã»ãã±ãŒã¿ (Service Locator)
Locator.GetService<IProductService>()
ã®ããã«genericãªåparameterãæž¡ããšãäºåã«ç»é²ãããå®è£
ã®åããããšããservice locatorã«ãã£ãŠäŸåã解決ãããã¿ãŒã³ã
å¿
èŠãªäŸåãconstructorã§æ瀺ãããªãã®ã§ãå©çšåŽã¯ããããããservice locatorãå©çšããã¯ã©ã¹ãå¿
èŠãšããserviceãç»é²ããŠãããªããšãããªãã
å€æŽã«ãã£ãŠæ°ããserviceã®è§£æ±ºãå¿
èŠã«ãªã£ããšããŠãããããåã«æ瀺ãããŠããªãã®ã§ãå®è¡æã«ãšã©ãŒã«ãªã£ãŠããŸãã
ã¢ã³ããšã³ãã»ã³ã³ããã¹ã (Ambient Context)
åæåºç¹ã®å€ã§ãæ®çºæ§äŸåãžã®ã°ããŒãã«ãªã¢ã¯ã»ã¹ãæäŸããããšã
å
žåçã«ã¯ãçŸåšæå»ã®ååŸãããã®ã³ã°ã
çŸåšæå»ã®ååŸã¯ããã¹ãã®èŠ³ç¹ãããDIããŠãããããã®ã³ã°åŠçããã°ããŒãã«ã«loggerãååŸããã®ã¯ã¢ã³ããã¿ãŒã³ãšãããŠããã
ãã ãloggerãDIãããšãªããšãã³ã³ã¹ãã©ã¯ã¿ãŒã®ããããšããã§ãloggerãèŠæ±ããå¿
èŠããããé床ãªæ³šå
¥(constructor over-injection)ãšåŒã°ããcode smellã«ã€ãªãã£ãŠããŸãã ã§ã¯ã©ãããããããã®è©±ã¯10ç« ã§èª¬æãããã
ç§ã¯ãloggingã«é¢ããŠã¯ãtracing
ãå©çšããŠããããã®äžã§ãtracing::info!("message")
ã«ããã«ããŠlogãåºåããŠããŸããããã®ãã¯ãã¯å
éšçã«ã¯ãglobalã®thread localã«ã¢ã¯ã»ã¹ããŠããŸãã
ãã¹ãã«é¢ããŠã¯
with_default;
/* ... */
assert_eq!;
ã®ãããªç¹å®ã®é¢æ°å®è¡èªã«æå³ãããlogãåºåããããã®ãã¹ããæžããŠããã®ã§ãããšããŠããŸããã
ãŸãããã®åŠçã¯ãthread localã®å€æ°ã§è¡ãããã®ã§ãtestã®äžŠè¡æ§ã§ãåé¡ãªããšèããŠããŸãã
å¶çŽã«çžãããçæ (Constrained Construction)
æœè±¡ã®å®è£
ã«éããŠãç¹å®ã®å®è£
ã«åŒãã¥ãããã·ã°ããã£ãå©çšããŠããŸãããšã
äŸãã°ãIProduceRepository
ã®ã³ã³ã¹ãã©ã¯ã¿ãšããŠãSqlProductRepository
ã«åŒãã¥ãããŠãåŒæ°ã«æåååã®connection stringãèŠæ±ããåã«ããŠããŸãçã
ãã®äŸã¯ãããã«äœ¿ãã¥ããã®ã§ãããªããšæãããæœè±¡ã®å®çŸ©ã«éããŠãå®è£
ãæŒããŠããŸãã®ã¯ãããããšæãã
èªåã課é¡ã«æã£ãŠããã®ã¯ãRepository
ã®æœè±¡(trait)ãå®çŸ©ããã«éããŠãRDSã®ãã©ã³ã¶ã¯ã·ã§ã³ãšãNoSQL(DynamoDBããMongoDB)ã®æžã蟌ã¿å¶çŽãã©ããã£ãŠåæ ãããã ãšæã£ãŠããããã©ã³ã¶ã¯ã·ã§ã³ãæžã蟌ã¿å¶çŽãæœè±¡ã«åæ ããªããšãRDSã®å®è£
ã§ã¯ãå€æŽãåŸç¶ã®readåŠçããããèŠããããMongoã®å®è£
ã ãšã¿ããªããšãããããªããšãèµ·ããŠããŸã£ããã
第6ç« ã³ãŒãã®å«ãªèã (code smell)
ã³ã³ã¹ãã©ã¯ã¿çµç±ã§ã®é床ãªæ³šå ¥ (Constructor Over-Injection)
åºæ¬çã«ã³ã³ã¹ãã©ã¯ã¿çµç±ã§ã®äŸåæ³šå ¥ãçšããã¹ããªã®ã§ãçŽ çŽã«ãã®éãã«ãããšãã以äžã®ãããªã³ãŒãã«ãªã£ãã
ãã®ããã«äŸåãå€ãå Žåã¯ãåäžè²¬ä»»ã®ååã«éåããŠããå
åãªã®ã§ã泚å
¥ããäŸåã®æ°ãæžããããã
ãããªãšãã«ã©ããã£ãæ¹æ³ãããããšãã解説ããããŠããŸãã
泚å
¥ãããäŸåã®æ°ãå¢ããŠãã£ãŠããŸãç¹ã«ã€ããŠã¯èª²é¡ã«æããããšãå€ãã£ãã®ã§ãæ¬ç« ã§çŽ¹ä»ãããã¢ãããŒããè©ŠããŠã¿ãããšæã£ãŠããŸãã
æœè±¡ãã¡ã¯ã㪠(abstract factory) ã®èª€çš
æåããçŽ çŽã«äŸåã泚å
¥ããã°ããã ãã§ã¯ãšæã£ãŠããŸã£ãã®ã§å²æã
ã€ã³ã¿ãŒãã§ã€ã¹åé¢ã®ååã®èª¬æãããããããã£ãã§ãã
埪ç°äŸå (cyclic dependency)
æ°ãã«ç£æ»èšŒè·¡(Audit Trail)ãè¿œå ããããã«ãSqlUserRepository
ã¯ã©ã¹ã«AuditTrailAppender
ã泚å
¥ããããã埪ç°äŸåã«é¥ã£ãŠããŸã£ãã±ãŒã¹ãäŸã«ã解æ¶ã®ã¢ãããŒãã解説ããŠãããŸãã
埪ç°äŸåã¯ãåäžè²¬ä»»ã®ååéåã®å
åã§ãããã€ã³ã¿ãŒãã§ãŒã¹ãåé¢ããŠãäŸåã解æ¶ããæ¹æ³ã¯éåžžã«åèã«ãªããŸããã
第3éš çŽç²ãªäŸåæ³šå ¥ (Pure DI)
第7ç« ãªããžã§ã¯ãåæ (object composition)
Windows, .NETåºæã®è©±ãå€ãã£ãã®ã§å²æã
第8ç« ãªããžã§ã¯ãã®çåæé (lifetime)
泚å
¥ãããäŸåãã¹ã¬ããã»ãŒãã§ãªãå ŽåããäŸåãå©çšããã¯ã©ã¹ããæå¹æéãçãå Žåçã«é¢ãã泚æäºé
ã
Rustã§ã¯Send, Syncãborrow checkerãšãã£ãèšèªäžã®ä»çµã¿ã§ãéåããŠãããã³ã³ãã€ã«ãéããªãã®ã§ãæãããäŸå (Captive Dependency)ã®ããã«ããããååãã€ããŠè«ããªããŠãããã
äŸåã®çæã³ã¹ãã®èŠ³ç¹ãããçæåŠçãé
延ãããããã«Lazy<T>
ãçšããå Žåã«ã€ããŠã®èª¬æãããã
ãã ãLazy
åãå°å
¥ããããããã®åé¡ç¹ãææãã€ã€ããLazy<T>
ã®æ³šå
¥èªäœã¯ééã£ãŠããããã§ã¯ãªããšèª¬æãããŠãããèŠé ãããªãã£ãã
æœè±¡ãæŒæŽ©ãããªããšãã芳ç¹ãã
use LazyCell;
ã®ããã«åŒæ°ã®åã«LazyCell
ãèŠæ±ããã®ã§ã¯ãªã
ã®ããã«ããŠãLazyã®å©çšã¯æ³šå
¥åŽã§è¡ããå©çšããåŽã«æèãããªãæ¹æ³ã玹ä»ãããŠããã
ããã¯æžããŠããŸããããªã³ãŒãã ã£ãã®ã§æ°ãã€ããŠããããã
第9ç« ä»å ¥ (interception)
ããããDecoratorãã¿ãŒã³ã«ã€ããŠã暪æçé¢å¿äºãšããŠãç£æ»èšŒè·¡(Audit Trail)ããµãŒããããã¬ãŒã«ãäŸã«å®éã«æ¢åã®åŠçã«ããããè¿œå ããäŸãã¿ãŠããã
ç§ã®æèŠãšããŠã¯ãåäžã®æœè±¡(interface)ãç¶æããŠãåŠçãwrapããŠããã®ã¯ããšã©ãŒã®ååšãèæ
®ãããšããããªã«åçŽãããªãã®ããªãšæãã
äŸãã°ãrepositoryã®UpdateUser
ã®åŒã³åºããwrapããŠãauditã®èšé²ããšãå Žåã§ããauditã®å€±æãšãããšã©ãŒãæ°ããçºçããããã«ãªãã®ã ãããåŒã³åºãåŽã®ãšã©ãŒãã³ããªã³ã°ã¯åœ±é¿ãåãããšæããauditã®èšé²ã倱æããå Žåãhashicorpã®vaultã®ããã«åŠçèªäœã倱æãããã®ããåŠçèªäœã¯ç¶ç¶ããã®ãã«ãã£ãŠãããšã©ãŒãã³ããªã³ã°ã®æ¹éã¯å€ãããšæãã®ã§ãããã®äŸã§çŽ¹ä»ãããŠããããã«wrapããŠåŒã³åºãåŽã«æèãããã«åŠçãè¿œå ã§ããã®ãçåã ã£ãã
åŠçãwrapããŠãããšããã°ãtowerã®Serviceãããããããã§ããšã©ãŒãã©ãè¡šçŸãããã§è°è«ããã£ãã
ãšã©ãŒãAuditError<RepositoryError>
ã®ããã«å
·äœåã«ãããBox<dyn Error>
ã«ããã°ãåŒã³åºãåŽã«AuditåŠçã®wrapãæèãããã«è¿œå ãå¯èœã ãã©ãä»åºŠã¯compileæã«AuditErrorããã¡ããšãã³ããªã³ã°ãããŠãããã確ãããããšãã§ããªãã(error.downcast_ref::<AuditError>()
ããå¿
èŠããããã)
ãšããããäžã€ã®åŠçã«æšªæçé¢å¿äºãèšè¿°ããŠãããšåŠçãcomposeã§ãªããªã£ãŠããŸãã®ã§ãrustã«ãã£ã圢ã§ããããã£ãåŠçãæžããããã«ãããã
第10ç« èšèšã ãã§å®çŸããã¢ã¹ãã¯ãæåããã°ã©ãã³ã° (Aspect-Oriented Programming: AOP)
æ©èœãè¿œå ããŠãã£ãçµæãè¥å€§åããProductService
interfaceãSOLIDååã®èŠ³ç¹ããåæããinterfaceãåå²ããŠãããªãã¡ã¯ã¿ãè¡ããŸãã
ãŸã9ç« ã®ä»å
¥ã§çŽ¹ä»ãããŠããæ¹æ³ã§ã暪æçé¢å¿äº(Audit Trail)ãå®è£
ããããšãããšãwrap(decorate)ããåŠçãéè€ããŠããŸãåé¡ã«ã察åŠããŸãã
å®éã«æçµåã¯ãšãŠã綺éºã«ãªã£ãŠãããéåžžã«åèã«ãªããŸããã
SOLIDåå(Single Responsibility, Open/Closed, Liskov Substitution, Interface Segragation)ãããããã®èŠ³ç¹ããéåããŠããç¹ãå
·äœçã«ææãããŠããŠããããããã£ãã§ãã
第11ç« ããŒã«ãçšããã¢ã¹ãã¯ãæåããã°ã©ãã³ã°
.NETã®ããŒã«ã®äœ¿ãæ¹ãªã®ã§å²æ
第4éš DI ã³ã³ãã
.NETãåæã«ããDIã³ã³ãããšåçš®ã©ã€ãã©ãªã®è©±ãªã®ã§å²æ
ãŸãšã
ç°¡åã«ã§ãããRustãæžãããã§åãå
¥ããããããšã¯ãªãããªãšãã芳ç¹ã§èªãã§ã¿ãŸããã
åºæ¬çã«ã¯.NETãªããOOPãåæã«ããŠããŸãããRustã«ãéããæããå€ããåèã«ãªããŸããã
æ¬æžãèªãã äžã§ã®çŸæç¹ã§ã®ã¹ã¿ã³ã¹ã§ããããšã«ãããã¹ããæžããããã³ãŒããç®æãããã§ãã
ãã¹ãã®æžãããããèããŠãããšãèªç¶ãšæ®çºæ§äŸåã泚å
¥ããããinterfaceã®ç²åºŠãå°ãããªã£ãããæœè±¡ããããªããªããšæã£ãŠããŸãã