0
votes

I started using Scala few weeks ago. Overall I really like all the features that this language gives to the developer, but it is hard to switch from Java habits sometimes.

This question is concerning traits. Currently almost everytime I see some part of logic that I would extract to different class in Java and add constructor parameter to dependent class I am switching to creating trait and mixing it in my class. (if all you have is a hammer, everything looks like a nail)

But I see two problems with my approach.

Testing classes with mixed in traits:

In Java if I would have class Foo that would use Bar and Baz classes I would probably inject them to my Foo class and use them. If I do this using traits I will have class Foo extends Bar with Baz.

Now in my tests I would have to write something like

trait BarMock extends Bar{
    override def bar = "barMock"
}

trait BazMock extends Baz{
    override def baz = "bazMock"
}

val foo = new Foo with BarMock with BazMock

if I want to swap the implementation of some trait behaviour. Maybe it is just me that is used to writing

Bar bar = mock(Bar.class);
when(bar.bar()).thenReturn("barMock");
Baz baz = mock(Baz.class);
when(baz.baz()).thenReturn("bazMock");
Foo foo = new Foo(bar, baz);

Is it normal to mock traits like I showed in Scala projects? Or I should restrain myself from mixing in mocked traits in tests.

Trait method visibility

This is also connected with my current obsession with traits. In Java if I inject Bar, Baz instances to my Foo instance, I am not automatically adding all public methods from Bar, Baz to my Foo interface. If I want to do it, I have to add each delegating method hand by hand. If I am using traits, when I mix in some trait, I am automatically "polluting" Foo interface with methods from my traits. Is there any way I could achieve something like private inheritance from C++?

The only solution that comes to my mind is declaring trait methods as protected and marking the trait as package private. This way methods will be visible in Foo, but outside of the package someone can't write val bar:Bar = new Foo. Also if I understand correctly, package private constraint will be validated only when package private trait source code will be in my project, because JVM has no way of representing the concept of package private and compiler just makes it public in bytecode.

1

1 Answers

1
votes

In a word: Cake. The Cake Pattern. Much has been written about it, most marginal, but once the concept clicks into your head it's pretty easy to realize. And it's a great tool for achieving SOLID as well as testability and statically typed dependency injection and mix-and-match componentized code.

There is a price to it, but where I used to feel that price demanded a specific justification to use Cake for a given bit of your code, I now see it the other way around. You should justify not using it on a case-by-case basis.

The seminal paper on Cake is Scalable Component Abstractions (that's a paywall, but it's available free elsewhere on the 'Net). Nonetheless, as with most such things, that's not the best place to start. Simply searching the Web for "Scala Cake Pattern" produces many hits. Read a few until you find one that englightens you…