DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations

Trending

  • How To Check IP Addresses for Known Threats and Tor Exit Node Servers in Java
  • Mastering Time Series Analysis: Techniques, Models, and Strategies
  • What Is Test Pyramid: Getting Started With Test Automation Pyramid
  • Apache Kafka vs. Message Queue: Trade-Offs, Integration, Migration
  1. DZone
  2. Coding
  3. Languages
  4. Implementing Factories in Scala & MacWire 0.3

Implementing Factories in Scala & MacWire 0.3

Adam Warski user avatar by
Adam Warski
·
Jun. 20, 13 · Interview
Like (0)
Save
Tweet
Share
4.01K Views

Join the DZone community and get the full member experience.

Join For Free

Factories are useful when we need to create multiple instances of a class at run-time, usually providing some parameters, but still without using new explicitly; we want to make some complex object creation abstract. The created object may depend both on runtime-provided data (the parameters), and some other “services”.

MacWire 0.3 adds support for some factories usage scenarios, while others can be implemented in pure Scala.

TL;DR:

  • MacWire 0.3 adds support for wiring using parameters of the method enclosing the wire[] macro
  • traditional approaches to defining injectable factories are verbose
  • but using Scala’s nested case classes it is possible to define factories in a simple, compact and readable way

As a running example, our aim will be to create a wrapper for a User domain object. We will want to wrap the user with a PriceCalculator, which calculates product prices taking into account user-specific discounts. To read the discounts, the calculator needs to access the database, using an instance of the DatabaseAccess class.

Hence ideally, we would want a partially-wired PriceCalculator, which has the database access provided, and which can be easily created for any given user. How to implement this using Scala and MacWire?

FACTORIES IN MODULES

Firstly, factories may be parts of the “modules” which contain the wired instances. In such case, instead of a val, we should use a def.

The new feature in MacWire 0.3 is that the enclosing method’s parameters are also used for wiring.

For example:

case class User
class DatabaseAccess
class PriceCalculator(databaseAccess: DatabaseAccess, user: User)
 
trait ShoppingModule extends Macwire {
   lazy val databaseAccess = wire[DatabaseAccess]
   def priceCalculator(user: User) = wire[PriceCalculator]
}

In this case the macro will expand the code to:

trait ShoppingModule extends Macwire {
   lazy val databaseAccess = new DatabaseAccess
   def priceCalculator(user: User) = new PriceCalculator(databaseAccess, user)
}

Note that this would also work if the module was nested in the def; hence we can create whole wired object graphs, using method parameters for wiring (“sub-modules”).

As in factories the parameters often represent data, there may be several parameters of the same type (this includes primitives). That’s why when wiring using enclosing method parameters, if multiple matches for a type are found, an additional by-name matching step is added.

INJECTABLE FACTORIES

This works very well in modules, but what if we need to pass the factory as a parameter to another class? For example, if we have a SpecialOfferMailer, which needs to create per-userPriceCalculators?

We could pass in a function object, and declare the dependency as follows:

class SpecialOfferMailer(priceCalculator: User => PriceCalculator)

This can be also viewed as a partially applied PriceCalculator constructor.

This could work well in simple cases, but has three drawbacks:

  • we need to repeat the whole function signature whenever we declare the dependency (however a type alias could help here)
  • we loose parameter names, which can cause code readability problems if our factory accepts primitives or multiple parameters of the same type
  • special support from MacWire would be needed to automatically convert defs to function objects, or a separate function object val would have to be defined manually.

We could also take the traditional route of defining a separate factory trait, e.g.:

class PriceCalculator(databaseAccess: DatabaseAccess, user: User)
 
object PriceCalculator {
   trait Factory {
      def create(user: User): PriceCalculator
   }
}
 
class SpecialOfferMailer(priceCalculatorFactory: PriceCalculator.Factory) { ... }

then we could also have some (not yet implemented) support for wiring such factories using MacWire, e.g.:

trait ShoppingModule {
   lazy val priceCalculatorFactory 
          = wireFactory[PriceCalculator, PriceCalculator.Factory]
   ...
}

which would expand to:

trait ShoppingModule {
   lazy val priceCalculatorFactory = new PriceCalculator.Factory {
      // the wire here uses values from the method parameters and from the 
      // outer module
      def create(user: User) = wire[PriceCalculator]
   }
   ...
}

However again, this has drawbacks:

  • pretty verbose – separate trait
  • needs special-case support for auto-wiring
  • we need to repeat the factory parameters in the create def and in the class constructor

SCALA FACTORIES

There is however another way – using case classes in a bit untypical manner we can have an elegant factory implementation. For example:

class PriceCalculatorFactory(databaseAccess: DatabaseAccess) {
   case class create(user: User) {
      // methods
   }
}
 
class SpecialOfferMailer(priceCalculatorFactory: PriceCalculatorFactory)
 
// Nothing special here. Just plain ol' MacWire
trait ShoppingModule extends Macwire {
   lazy val databaseAccess = wire[DatabaseAccess]
   lazy val priceCalculatorFactory = wire[PriceCalculatorFactory]
   lazy val specialOfferMailer = wire[SpecialOfferMailer]
}

Note that using this approach the service-parameters and the data-parameters are nicely separated (former being the main class parameters, latter being the nested case class parameters). Also no parameter list definition is repeated! And we don’t need any special support for auto-wiring.

Because the nested class is a case class, using create looks like a method invocation, while in fact it is a constructor of a new object, e.g.:

class SpecialOfferMailer(...) {
   def mailOfferOfTheDay(user: User) {
      val priceCalculator = priceCalculatorFactory.create(user)
      products.foreach { product => 
          mailOffer(user, product, priceCalculator.price(product)) }
   }
}

The type of the per-user price calculator object is PriceCalculatorFactory#create. This is a bit ugly, especially if need to pass it around, but we could add e.g. to the package object a type alias:

type PriceCalculator = PriceCalculatorFactory#create

The sources for MacWire are available on GitHub under the Apache2 license; and the binary release is in the central repository.

Have fun, and let me know what do you think about the Scala-factories implementation!

Factory (object-oriented programming) Scala (programming language) Object (computer science)

Published at DZone with permission of Adam Warski, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • How To Check IP Addresses for Known Threats and Tor Exit Node Servers in Java
  • Mastering Time Series Analysis: Techniques, Models, and Strategies
  • What Is Test Pyramid: Getting Started With Test Automation Pyramid
  • Apache Kafka vs. Message Queue: Trade-Offs, Integration, Migration

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: