简体   繁体   中英

Scala, Cake Pattern, and MacWire

I've implemented an Email service using the cake pattern. Here below is the EmailComponent , which provides functionality for styling the email body:

trait EmailComponent {
  def body: Body

  trait Body {
    def style(content Html): Html
  }
}

trait DefaultEmailComponent extends EmailComponent {
  def body = new DefaultBody

  class DefaultBody extends Body {
    views.html.email(content)
  }
}

... and here is the EmailServiceComponent that actually implements the Email service using an EmailComponent :

trait EmailServiceComponent {
  def emailService: EmailService

  trait EmailService {
    def sendEmail(from: String, recipients: Seq[String], subject: String, content: Html)
  }
}

trait DefaultEmailServiceComponent extends EmailServiceComponent {
  this: EmailComponent =>

  def emailService = new DefaultEmailService

  class DefaultEmailService extends EmailService {
    def sendEmail(from: String, recipients: Seq[String], subject: String, content: Html) {
      val htmlBody = body.style(content)
      EmailHelper.sendEmail(from, recipients, Some(subject), (None, Some(htmlBody)))
    }
  }

The code above works fine... but I was surfing on the Internet when I came across MacWire. I read some documentation here and there and found really interesting, but to be honest I haven't fully understood how to use it and how it works. Having said that, how could I reimplement the example above with MacWire?

Several things to consider:

  1. One big difference is that the cake pattern in your example uses inheritance / class composition to satisfy dependencies and build the concrete instances, whereas with dependency injection you would mostly use delegation. It's up to you to decide how tightly coupled you want the classes to be.

  2. There are limitations in MacWire to wiring when using traits defined inside other traits. So your Default... implementations would have to go outside of their parent traits.

  3. Upon a quick glance, it appears that MacWire cannot resolve concrete implementations of a trait (unlike Guice, a well-established dependency-injection framework for Java, where you could use bindings and annotations for that). This means you'd have to use wire[DefaultEmailService] instead of wire[EmailService] .

  4. There is no support for circular dependencies in MacWire. In the case above, you don't have them anyway: EmailServiceComponent depends on EmailService which in turn depends on EmailComponent.

So, with MacWire, your code would just be classes that use other classes, like

class DefaultEmailComponent extends EmailComponent { ... }
class DefaultEmailService(emailComponent: EmailComponent) extends EmailService { ... }
trait EmailServiceComponent {
    def emailService: EmailService
}
class DefaultEmailServiceComponent(val emailService: EmailService) 
                                              extends EmailServiceComponent { ... }

lazy val emailC: EmailComponent = wire[DefaultEmailComponent]
lazy val emailSvc: EmailService = wire[DefaultEmailService]
lazy val emailSvcC: EmailServiceComponent = wire[DefaultEmailServiceComponent]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM