{"id":16782804,"url":"https://github.com/davidmoten/cake-pattern","last_synced_at":"2025-03-23T15:31:15.372Z","repository":{"id":12930034,"uuid":"15607746","full_name":"davidmoten/cake-pattern","owner":"davidmoten","description":"Examples of cake pattern in scala for injecting singleton and non-singleton dependencies","archived":false,"fork":false,"pushed_at":"2021-01-29T04:45:48.000Z","size":28,"stargazers_count":36,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-02T01:55:23.884Z","etag":null,"topics":["cake-pattern","constructor","injection","pattern","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidmoten.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-01-03T11:14:07.000Z","updated_at":"2024-11-10T10:01:30.000Z","dependencies_parsed_at":"2022-07-09T23:47:00.768Z","dependency_job_id":null,"html_url":"https://github.com/davidmoten/cake-pattern","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fcake-pattern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fcake-pattern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fcake-pattern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmoten%2Fcake-pattern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidmoten","download_url":"https://codeload.github.com/davidmoten/cake-pattern/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244312649,"owners_count":20432929,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cake-pattern","constructor","injection","pattern","scala"],"created_at":"2024-10-13T07:48:14.788Z","updated_at":"2025-03-23T15:31:14.936Z","avatar_url":"https://github.com/davidmoten.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"cake-pattern\n============\nAs a long term user of Guice for injecting class dependencies into constructors I was curious as to how the cake pattern in Scala worked.\n\nI read [Jonas Boner's blog article](http://jonasboner.com/real-world-scala-dependency-injection-di/) and followed \nit but thought the example could have been clearer and the method described a bit more succintly for the impatient reader like myself.\n\nSo what is the cake pattern?\n----------------------------- \n\nThe cake pattern uses features of self types and mixins in Scala to enable apparently parameter-less construction of objects.\n\nLets convert the classes below to use dependency injection (DI) with the cake pattern:\n\n```scala\ntrait Configuration {\n  def value: String\n}\n\nclass DefaultConfiguration extends Configuration {\n  val value = \"production\"\n}\nclass TestingConfiguration extends Configuration {\n  val value = \"test\"\n}\n    \nclass A(configuration:Configuration){\n  val value = \"a-\" + configuration.value\n}\nclass B(configuration:Configuration, a:A){\n  val value = a.value + \"-b-\"+ configuration.value\n} \n```\n\nThis is how you would instantiate the classes without DI:\n```scala\nval configuration = new Configuration\nval a = new A(configuration)\nval b = new B(configuration,a)\n```\n\nTo apply the cake pattern: \n\n* wrap *Configuration*, *A* and *B* with their own traits (*Component* traits)\n* move constructor dependencies to Components using self types   \n* add an abstract instance to each Component trait \n* create a *Registry* object that instantiates everything \n\nLet's do it for our example classes:\n\n```scala\ntrait ConfigurationComponent {\n  val configuration: Configuration\n}\n\ntrait AComponent {\n  this: ConfigurationComponent =\u003e\n  val a: A\n  class A {\n    val value = \"a-\" + configuration.value\n  }\n}\n\ntrait BComponent {\n  this: ConfigurationComponent with AComponent =\u003e\n  val b: B\n  class B {\n    val value = a.value + \"-b-\"+ configuration.value\n  }\n}\n\ntrait Components\n    extends ConfigurationComponent\n    with AComponent\n    with BComponent\n\nobject Registry extends Components {\n  val configuration = new DefaultConfiguration\n  val a = new A()\n  val b = new B()\n}\n\nobject RegistryTesting extends Components {\n  val configuration = new TestingConfiguration\n  val a = new A()\n  val b = new B()\n}\n```\n\nNow to get a singleton wired up immutable instance of *B* we call ```Registry.b``` (or ```RegistryTesting.b```) and the important thing to notice \nis that to instantiate *B* within the *Registry* object we just called ```new B()``` without any constructor parameters.\n\nObviously there's some boilerplate involved with setting up the cake pattern, in fact as design patterns go I'd call it bit noisy, somewhat verbose \n but still elegant. For less verbosity one might consider [Subcut](https://github.com/dickwall/subcut) or Guice.\n\nFull source code for this example is in package examples.example1 in \n[src/main/scala/examples.scala](https://github.com/davidmoten/cake-pattern/blob/master/src/main/scala/examples.scala)\n\nInjecting non-singleton instances\n-----------------------------------\nThe above example  demonstrates how to wire up singleton instances (singleton in terms of the scope of Registry) of *A*,*B* and *Configuration*. \nAnother common use case for injection is to wire in non-singleton instances (that themselves may have dependencies \non singleton or non-singleton instances). Example 2 demonstrates this:\n\nThese are the classes we are going to convert using the cake pattern:\n\n```scala\nclass A(configuration:Configuration, c:C){\n  val value = \"a-\" + configuration.value + \"-\" + c.value\n}\nclass B(configuration:Configuration, a:A, c:C){\n  val value = a.value + \"-b-\" + configuration.value + \"-\" + c.value\n} \nclass C(configuration:Configuration){\n  val value = \"c-\" + configuration.value + \"-\" +\n        randomUUID.toString.substring(1, 5)\n}\n```\nThis is how you would instantiate the classes without DI:\n```scala\nval configuration = new Configuration\nval a = new A(configuration,new C(configuration))\nval b = new B(configuration,a,new C(configuration))\n```\n\nTo wire in a non-singleton instance *C* into *A* and another into *B*:\n* wrap *C* with a *Component* trait\n* define constructor dependencies using self types\n* do *NOT* add an abstract instance of *C* to *CComponent* trait\n* instantiate *C* inside class *A* and class *B* using ```val c = new C()```\n\nThis is how it looks:\n```scala\n import java.util.UUID._\n  \n  trait AComponent {\n    this: ConfigurationComponent with CComponent =\u003e\n    val a: A\n    class A {\n      val c = new C()\n      val value = \"a-\" + configuration.value + \"-\" + c.value\n    }\n  }\n\n  trait BComponent {\n    this: ConfigurationComponent with AComponent with CComponent =\u003e\n    val b: B\n    class B {\n      val c = new C()\n      val value = a.value + \"-b-\" + configuration.value + \"-\" + c.value\n    }\n  }\n\n  trait CComponent {\n    this: ConfigurationComponent =\u003e\n    class C {\n      val value = \"c-\" + configuration.value + \"-\" +\n        randomUUID.toString.substring(1, 5)\n    }\n  }\n\n  object Registry\n    extends ConfigurationComponent\n    with AComponent\n    with BComponent\n    with CComponent {\n\n    val configuration = new DefaultConfiguration\n    val a = new A()\n    val b = new B()\n  }\n```\n\nFull source code for this example is in package examples.example2 in \n[src/main/scala/examples.scala](https://github.com/davidmoten/cake-pattern/blob/master/src/main/scala/examples.scala)\n\nCompiling and testing the examples\n------------------------------------------\nGet the source:\n\n    cd \u003cYOUR_WORKSPACE\u003e\n    git clone https://github.com/davidmoten/cake-pattern.git\n    cd cake-pattern\n    \nBuild with maven:\n\n    mvn clean test\n    \nor build with sbt:\n\n    sbt clean test\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Fcake-pattern","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidmoten%2Fcake-pattern","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmoten%2Fcake-pattern/lists"}