Mixes versus composition in scala

In the java world (more precisely, if you do not have multiple inheritance / mixins), the rule of thumb is pretty simple: "Use the composition of objects over class inheritance."

I would like to know if / how this has changed if you are also considering mixins, especially in scala?
Are blends considered a multiple inheritance method, or a more cool composition?
Is there also an “Object support structure over a class” (or vice versa)?

I saw several examples when people use (or abuse) mixins, when composing an object can also do the job, and I'm not always sure which one is better. It seems to me that you can achieve quite similar things with them, but there are some differences: some examples:

  • visibility - with mixins everything becomes part of the public api, which does not correspond to the composition.
  • verbosity - in most cases mixins are less verbose and a little easier to use, but this is not always the case (for example, if you also use self types in complex hierarchies)

I know that the short answer is: “It depends,” but there is probably a typical situation where it is or is better.

Some examples of recommendations that I could come up with so far (if I have two traits A and B, and A wants to use some methods from B):

  • If you want to extend API A using the methods from B, then mixins, otherwise composition. But this does not help if the class / instance that I am creating is not part of the public API.
  • If you want to use some patterns that need mixes (like the Stackable Trait Pattern ), then this is a simple solution.
  • If you have circular dependencies, mixins with self types can help. (I try to avoid this situation, but it is not always easy)
  • If you need any dynamic runtime solutions, how to execute composition, then the composition of the object.

In many cases, mixins seem lighter (and / or less verbose), but I'm pretty sure that they also have some pitfalls, such as the “God class” and others described in two articles by artima: part 1 , part 2 ( By the way, it seems to me that most of the other problems are not related / not related to scala).

Do you have more such tips?

+67
scala mixins composition
Aug 6 2018-10-06T00:
source share
2 answers

Many problems that people with mixes can encounter can be prevented in Scala if you only mix abstract features in class definitions and then mix relevant concrete features when creating an object. For example,

trait Locking{ // abstract locking trait, many possible definitions protected def lock(body: =>A):A } class MyService{ this:Locking => } //For this time, we'll use a java.util.concurrent lock val myService:MyService = new MyService with JDK15Locking 

There are some tips in this design. Firstly, this prevents the explosion of classes, since various combinations of functionality are needed. Secondly, it makes it easy to test, because you can create and mix specific "do nothing" features, similar to the layout of objects. Finally, we completely hid the binding function used, and even this blocking continues from the consumers of our service.

Since we got most of the claimed mixing flaws, we are still left with a trade-off between mixing and composition. For myself, I usually make a decision based on whether the hypothetical delegation object was completely encapsulated by the containing object or whether it could be divided and have its own life cycle. Locking is a good example of fully encapsulated delegates. If your class uses a lock object to control concurrent access to its internal state, this lock is fully controlled by the containing object, and neither it nor its operations are advertised as part of the class’s public interface. For fully encapsulated functions like this, I use mix-ins. For something in common, such as a data source, use composition.

+37
Aug 6 '10 at 19:30
source share

Other differences you did not mention:

  • Classes of classes have no independent existence:

( Scala programming )

If you find that a particular trait is most often used as the parent of other classes, so that child classes behave as the parent trait, then instead consider defining the trait as a class to make this logical relationship clearer.
(We say that it behaves like, not a, because the former is a more accurate definition of inheritance based on the Liskov principle of substitution - see, for example, [Martin2003].)

[Martin2003]: Robert C. Martin, Agile Software Development: Principles, Patterns, and Practice, Prentice-Hall, 2003

  • mixins ( trait ) do not have constructor parameters.

Therefore, advice, from Scala programming :

Avoid specific fields in properties that cannot be initialized with suitable default values.
Use abstract fields instead of or transform the attribute into a class with a constructor .
Of course, stateless traits do not have problems with initialization.

It is a general principle of good object-oriented design that the instance should always be in a known acceptable state, starting from the moment the construction process is completed .

This last part, dealing with the initial state of an object, often helped decide between the class (and class) and the trait (and mixins) for a given concept.

+10
Aug 6 '10 at 10:17
source share



All Articles