scala - Abstract fields for dependency injection -
in scala, there there wrong using below method of dependency injection.
// define interface trait filestorage { def readfile(filename:string):outputstream } // , implementation class s3filestorage extends filestorage { def readfile(filename:string):outputstream = ??? } // define our service trait abstract fields need // injected in order construct. implementation details go here. trait filehttpserver { val filestorage:filestorage def fetchfile( session:session, filename:string ) = ??? }
now wire things up
// wire concrete file service use in code // no implementation details should go here, we're wiring filehttpserverl // entire project wired way in central location if desired. object s3filehttpserver extends filehttpserver { val filestorage = new s3filestorage } // anonymously val myhttpserver = new filehttpserver { val filestorage = new s3filestorage } // or create mocked version testing val mockedhttpserver = new filehttpserver { val filestorage = mock[filestorage] }
obviously cake pattern provides more flexibility (particularly around self-types), simpler use cases has less boilerplate, while still providing compile time checking , clean unambiguous interface.
yes, absolutely fine approach. , yes, can use constructor injection, nothing wrong too. constructor injection have propagate dependencies manually, while cake pattern dependencies propagated automatically via self-type annotations. big projects constructor injection lead more boilerplate cake pattern, @ construction site (where create objects , set dependencies between them).
however, have presented not full-fledged cake pattern. in real cake pattern there additional layer around business logic classes, so-called components, , not wire logic classes directly components instead.
trait filestoragecomponent { def filestorage: filestorage trait filestorage { def readfile(filename: string): outputstream } } trait s3filestoragecomponent extends filestoragecomponent { val filestorage = new s3filestorage class s3filestorage extends filestorage { def readfile(filename: string): outputstream = ??? } } trait filehttpservercomponent { self: filestoragecomponent => val filehttpserver = new filehttpserver class filehttpserver { def fetchfile(session: session, filename: string) = ??? } } // wiring object s3filehttpserver extends filehttpservercomponent s3filestoragecomponent // anonymous val server = new filehttpservercomponent s3filestoragecomponent // mocking object testfilehttpserver extends filehttpservercomponent filestoragecomponent { val filestorage = mock[filestorage] }
in approach there more boilerplate in traits definitions, in return have greater flexibility , clear dependency management on use place. example, here how program entry point in 1 of projects looks like:
object main extends mainui defaultactorsmanagercomponent defaultpreferencesaccesscomponent defaultmodelcomponent defaultmainwindowviewcomponent defaultmainwindowcontrollercomponent mainwindowreportercomponent defaultclientactorcomponent defaultresponseparseractorcomponent defaultarchiveractorcomponent defaultmainwindowaccessactorcomponent defaulturlparsercomponent defaultlisteneractorcomponent defaultxmlprettifiercomponent
all main program components in 1 place. pretty neat imo.
Comments
Post a Comment