6 Mixins¶
Mixins are an interesting feature of Dart that you might not be familiar with, even if you know other programming languages. They’re a way to reuse methods or variables among otherwise unrelated classes.
Note
For you Swift developers, Dart mixins work like protocol extensions.
Before showing you what mixins look like, you’ll first take a look at why you need them.
Problems With Extending and Implementing¶
Think back to the Animal
examples again. Say you’ve got a bunch of birds, so you’re carefully planning an abstract class to represent them. Here’s what you come up with:
abstract class Bird {
void fly();
void layEggs();
}
“It’s looking good!” you think. “I’m getting the hang of this.” So you try it out on Robin
:
class Robin extends Bird {
@override
void fly() {
print('Swoosh swoosh');
}
@override
void layEggs() {
print('Plop plop');
}
}
“Perfect!” You smile contentedly at your handiwork.
Then you hear a sound behind you.
“Munch, munch. Glide, glide. Plop, plop. I’m a platypus.”
Oh. Right. The platypus.
Here’s the code you wrote for Platypus
back in Chapter 3, “Inheritance”:
abstract class Animal {
bool isAlive = true;
void eat();
void move();
@override
String toString() {
return "I'm a $runtimeType";
}
}
class Platypus extends Animal {
@override
void eat() {
print('Munch munch');
}
@override
void move() {
print('Glide glide');
}
void layEggs() {
print('Plop plop');
}
}
Your layEggs
code for Robin
is exactly the same as it is for Platypus
. That means you’re duplicating code, which violates the DRY principle. If there are any future changes to layEggs
, you’ll have to remember to change both instances. Consider your options:
- Platypus can’t extend
Bird
orRobin
, because platypi can’t fly. - Birds probably shouldn’t extend
Platypus
, because who knows when you’re going to add thestingWithVenomSpur
method? - You could create an
EggLayer
class and haveBird
andPlatypus
both extend that. But then what about flying? Make aFlyer
class, too? Dart only allows you to extend one class, so that won’t work. - You could have birds implement
EggLayer
andFlyer
while Platypus implements onlyEggLayer
. But then you’re back to code duplication since implementing requires you to supply the implementation code for every class.
The solution? Mixins!
Mixing in Code¶
To make a mixin, you take whatever concrete code you want to share with different classes, and package it in its own special mixin class.
Write the following two mixins:
mixin EggLayer {
void layEggs() {
print('Plop plop');
}
}
mixin Flyer {
void fly() {
print('Swoosh swoosh');
}
}
The mixin
keyword indicates that these classes can only be used as mixins. You can also use a normal class as a mixin as long as that class doesn’t extend another non-Object
class. So if you wanted to use EggLayer
as a normal class, then just replace the mixin
keyword with class
or abstract class
.
Now refactor Robin
as follows, using the with
keyword to identify the mixins:
class Robin extends Bird with EggLayer, Flyer {}
There are two mixins, so you separate them with a comma. Since those two mixins contain all the code that Bird
needs, the class body is now empty.
Refactor Platypus
as well:
class Platypus extends Animal with EggLayer {
@override
void eat() {
print('Munch munch');
}
@override
void move() {
print('Glide glide');
}
}
The layEggs
logic has moved to the mixin. Now both Robin
and Platypus
share the code that the EggLayer
mixin contains. Just to make sure it works, run the following code:
final platypus = Platypus();
final robin = Robin();
platypus.layEggs();
robin.layEggs();
Four plops, and all is well.
Challenges¶
Before moving on, here are some challenges to test your knowledge of mixins. It’s best if you try to solve them yourself, but solutions are available with the supplementary materials for this book if you get stuck.
Challenge 1: Calculator¶
- Create a class called
Calculator
with a method calledsum
that prints the sum of any two integers you give it. - Extract the logic in
sum
to a mixin calledAdder
. - Use the mixin in
Calculator
.
Challenge 2: Heavy Monotremes¶
Dart has a class named Comparable
, which is used by the sort
method of List
to sort its elements.
- Add a
weight
field to thePlatypus
class you made earlier. - Then make
Platypus
implementComparable
so that when you have a list ofPlatypus
objects, callingsort
on the list will sort them by weight.
Key Points¶
- Mixins allow you to share code between classes.
- You can use any class as a mixin as long as it doesn’t extend anything besides
Object
. - Using the
mixin
keyword means that a class can only be used as a mixin.