-
DI in Scala: Cake Pattern pros & cons
Posted on April 29th, 2011 28 commentsI’ve been looking at alternatives for java-style DI and DI containers which would use pure Scala; a promising candidate is the Cake Pattern (see my earlier blog post for information on how the Cake Pattern works). FP enthusiast also claim that they don’t need any DI frameworks, as higher-order functions are enough.
Recently Debasish Ghosh also blogged on a similar subject. I think his article is a very good introduction into the subject.
Below are some problems I encountered with the Cake Pattern. (Higher-order functions are coming up in the next post.) If you have solutions to any of them, let me know!
Parametrizing the system with a component implementation
First of all, it is not possible to parametrize a system with a component implementation. Supposing I have three components:
DatabaseComponent,UserRepositoryComponent,UserAuthenticatorComponentwith implementations, the top-level environment/entry point of the system would be created as follows:val env = new MysqlDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent
Now to create a testing environment with a mock database, I would have to do:
val env = new MockDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent
Note how much of the code is the same. This isn’t a problem with 3 components, but if there are 20? All of them but one have to be repeated just to change the implementation of one component. This clearly leads to quite a lot of code duplication.
Component configuration
Quite often a component needs to be configured. Let’s say I have a
UserAuthenticatorComponentwhich depends onUserRepositoryComponent. However, the authenticator component has an abstract valencryptionMethod, used to configure the encryption algorithm. How can I configure the component? There are two ways. The abstract val can be concretized when defining the env, e.g.:val env = new MysqlDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent { val encryptionMethod = EncryptionMethods.MD5 }But what if I want to re-use a configured component? An obvious answer is to extend the
UserAuthenticatorComponenttrait. However if that component has any dependencies (which, in the Cake Pattern, are expressed using self-types), they need to be repeated, as self-types are not inherited. So a reusable, configured component could look like this:trait UserAuthenticatorComponentWithMD5 extends UserAuthenticatorComponent { // dependency specification duplication! this: UserRepositoryComponent => val encryptionMethod = EncryptionMethods.MD5 }If we don’t repeat the self-types, the compiler will complain about incorrect
UserAuthenticatorComponentusage.No control over initialization order
A problem also related to configuration, is that there is no type-safe way to assure that the components are initialized in the proper order. Suppose as above that the
UserAuthenticatorComponenthas an abstractencryptionMethodwhich must be specified when creating the component. If we have another component that depends onUserAuthenticatorComponent:trait PasswordEncoderComponent { this: UserAuthenticatorComponent => // encryptionMethod comes from UserAuthenticatorComponent val encryptionAlgorithm = Encryption.getAlgorithm(encryptionMethod) }and initialize our system as follow:
val env = new MysqlDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent with PasswordEncoderComponent { val encryptionMethod = EncryptionMethods.MD5 }then at the moment of initialization of
encryptionAlgorithm,encryptionMethodwill benull! The only way to prevent this is to mix in theUserAuthenticatorComponentWithMD5before thePasswordEncoderComponent. But the type checker won’t tell us that.Pros
Don’t get me wrong that I don’t like the Cake Pattern – I think it offers a very nice way to structure your programs. For example it eliminates the need for factories (which I’m not a very big fan of), or nicely separates dependencies on components and dependencies on data (*). But still, it could be better ;).
(*) Here each code fragment has in fact two types of arguments: normal method arguments, which can be used to pass data, and component arguments, expressed as the self type of the containing component. Whether these two types of arguments should be treated differently is a good question :).
What are your experiences with DI in Scala? Do you use a Java DI framework, one of the approaches used above or some other way?
Adam
-
Envers and Hibernate 4.0.0.Alpha2 – automatic listener registration
Posted on April 18th, 2011 4 commentsDevelopment of Hibernate4 is well under way, with the Alpha2 version released recently. It contains one major improvement which is quite significant for Envers, namely Integrators (see HHH-5562 & HHH-6081).
Using this mechanism it will no longer be necessary to add the 6 event listeners to your Hibernate configuration – all that is needed for Envers to work is to put it on the classpath! If any of your entities are annotated with
@Audited, they will get audited automatically (any changes will be written to the history tables).It is of course possible to turn off the automatic registration, as well as write your own integrators, which automatically register custom event listeners. The integrators are discovered using java’s service loader, by the presence of a
META-INF/services/org.hibernate.spi.Integratorfile in the jar, which should contain the name of the class implementing the integration functionality.Adam






Twitter