This seems to be so easy, but I always have immense problems to apply it:
I have two different classes and I would like them to have a set of common attributes and methods.
For instance, I both have a book and a song, and I would like them to both have an array with comments, I could use inheritance and put the comments array onto the super class. No problem. However, when I try to apply this pattern a second time, I have a problem, because there is no multiple inheritance in JS. So, I can’t inherit from the comment superclass and for instance from an audio or print superclass.
Now, I sometimes read about composition in that context - however, even finding a good example via google turned out to be pretty difficult. I can see how I would apply composition by using a mixing. I can also see how I could add further things to my class via aggregation. However, how do I compose a class out of multiple attributes and methods the class should have?
Can someone please post a clean example how composing a class (like an ED-model) would look like with E6 classes in the ember octane world?
I think @chriskrycho’s round-up of strategies to replace mixins is the best summary one could give. Unfortunately there isn’t one true answer or one single prescription, it depends on what the state is and where it’s going to live.
FWIW you may want to really consider how DRY you want your code to be in the first place. For example in my company’s codebase we have a couple mixins which are used many places but they only contain one property. IMO these don’t really help us much when it comes down to it and can actually make the code harder to unwind. Strict adherence to DRY can push you down some “suboptimal” paths where you’re chasing the wrong abstraction, or trying to abstract too soon just to avoid repeating yourself. I’d recommend this article called “AHA Programming” as food for thought on just how “DRY” you want to be. Not saying that what you want to do is bad by any means just wanted to throw that out there since you are refactoring and probably adapting your mental model to Octane and native classes as we all are.
I find the right way to think about DRY is “eventually DRY”.
If you try to do DRY up front, you can only surmise what might end up being the same and what might not. When has the code you imagined in your head ever turned out to be what you check in after you’ve written your tests?
Do something twice, shamelessly repeating yourself. Once you have two nearly identical solutions, you know what can truly be the same and what can’t. DRY emerges.
After the second copy, you might let other changes occur for a little bit. Perhaps a third implementation shows up. By that point, you know what is enduring and what isn’t.
At that point, use what you learned in the implementations to refactor to “as DRY as will really work without perpetual pervasive redesign.” You have a good DRY solution you can leave alone for a while.
Thanks a lot for your answers. A lot of good stuff. But I think I didn’t describe my problem well enough - and some of my issues are likely typescript related.
Let’s consider an app with ED models. Multiple models share common attributes and behaviour.
There are some models, which are sortable, while others have the ability to be commented on. So, I would like to be able to compose those models that are sortable with the shared attributes and methods of being sortable. Same for models that contain comments, which have features like arranging those comments in multiple rows.
Then I would like to be able to type the ember components that receive these models as arguments. So, components that deal with the sort order expect a model which would inherit (or something more appropriate) from sortable and therefore has all the needed functions and attributes. etc…
I haven’t found a good way to do that. I certainly can move at least the methods to services, but that has pretty much become an anti-pattern with DDD inspired architecture. But then, one probably shouldn’t worry about that too much. And there is an interesting question: As services are pretty much singletons, and models are instantiated multiple times - would moving all those methods to services would have a performance impact?