Afrikaans
Akan
Albanian
Amharic
Arabic
Armenian
Azerbaijani
Basque
Belarusian
Bemba
Bengali
Bihari
Bosnian
Breton
Bulgarian
Cambodian
Catalan
Cebuano
Cherokee
Chichewa
Chinese (Traditional)
Corsican
Croatian
Czech
Danish
Dutch
English
Esperanto
Estonian
Ewe
Faroese
Filipino
Finnish
French
Frisian
Ga
Galician
Georgian
German
Greek
Guarani
Gujarati
Haitian Creole
Hausa
Hawaiian
Hebrew
Hindi
Hmong
Hungarian
Icelandic
Igbo
Indonesian
Interlingua
Irish
Italian
Japanese
Javanese
Kannada
Kazakh
Kinyarwanda
Kirundi
Kongo
Korean
Krio (Sierra Leone)
Kurdish
Kurdish (Soranî)
Kyrgyz
Laothian
Latin
Latvian
Lingala
Lithuanian
Lozi
Luganda
Luo
Luxembourgish
Macedonian
Malagasy
Malay
Malayalam
Maltese
Maori
Marathi
Mauritian Creole
Moldavian
Mongolian
Myanmar (Burmese)
Montenegrin
Nepali
Nigerian Pidgin
Northern Sotho
Norwegian
Norwegian (Nynorsk)
Occitan
Oriya
Oromo
Pashto
Persian
Polish
Portuguese (Brazil)
Portuguese (Portugal)
Punjabi
Quechua
Romanian
Romansh
Runyakitara
Russian
Samoan
Scots Gaelic
Serbian
Serbo-Croatian
Sesotho
Setswana
Seychellois Creole
Shona
Sindhi
Sinhalese
Slovak
Slovenian
Somali
Spanish
Spanish (Latin American)
Sundanese
Swahili
Swedish
Tajik
Tamil
Tatar
Telugu
Thai
Tigrinya
Tonga
Tshiluba
Tumbuka
Turkish
Turkmen
Twi
Uighur
Ukrainian
Urdu
Uzbek
Vietnamese
Welsh
Wolof
Xhosa
Yiddish
Yoruba
Zulu
Now in the last section we looked at how inheritance can be used to share code that's common between
classes. And this works well with classes that have a natural inheritance relationship.
For example, your clumsy player is a kind of player and our 3D movie is a kind of movie.
Right, but sometimes we have code that we want to share across classes that don't have
an is a relationship. And Ruby has a wonderful alternative to inheritance called mixins.
A mixin is simply a module that has methods and those methods get mixed into other classes.
So for example, imagine a scenario where we want to model the concept of a song. We would
create a song class. And a song should be rankable, just like a movie. We can thumbs
up it or thumbs down it. And currently all that behavior lies in the movie class.
But we don't want to copy all that behavior into the song class. We'd end up with duplication.
And it doesn't make sense for a song to inherit from a movie. It doesn't pass the is a rule.
A song is not a kind of movie.
Instead we want to put all the rank related code in a module.
So let's say we have these two classes, a movie class and a song class. And suppose
we want to share some common behavior across these classes which aren't in the same inheritance
hierarchy. For example, to keep it simple, suppose we want to be able to call a thumbs
up method and print out their name. Well, we could write a thumbs up method inside of
each of these classes, but then we'd end up with duplication. So instead we can write
a module. We'll call the module rankable, like that. And then inside of the module we'll
define that thumbs up method.
Inside of the thumbs up method, all we're going to do for now, just to keep it simple,
is we're going to print out the title, Got a Thumbs Up. Just like that.
Now a couple things to note here. This is a regular instance method. Remember when we
defined methods before in modules, we would put self dot and then we would call it on
the module. Well in this case, we're defining an instance method, thumbs up.
Now we saw before that we can't create an instance of the rankable module. Therefore
we can't call this instance method directly. The way that we call it is we mix it into
these classes below. So down in movie, to mix in this module rankable, I say include
rankable and then down in song, I would do the same thing, include rankable like that.
Now inside of movie, or for a movie object, I can call it movie dot thumbs up and in the
same way I can take a song and call thumbs up on it. If we run that, sure enough, Goonies
got a thumbs up and Ruby Baby got a thumbs up. So they both have a thumbs up method that
came from this module. Something else to notice is the module or the instance method here,
we can use at title because the point at which this method gets run will be when it's down
inside of a movie and a movie has a title instance variable and so does a song.
So something kind of cool about this is mixed in modules effectively behave like super classes.
In other words, it changes the method lookup hierarchy. The module lives between the classes
that include the module and their direct super class. So when we call a method, it looks
in the class, then it looks in the module, then it looks in the super class and then
all the way to the top. We can look at this another way by loading up our mixins file
where we've got our song on our movie there and it printed out some code that we have,
some example code. But if we look at movies.movie.ancestors now, we've got movie, but then notice that
the module name rankable is between movie and the object. So Ruby is going to look here
for methods, then at rankable, then up to object. So it's inserted the module into the
lookup hierarchy here. In the same way, if we look at songs ancestors, rankable is between
it and object. And something kind of interesting, if we look at something like arrays ancestors,
we have the array class, but we have this module in here called enumerable. And that's
where all those methods like select, reject, and partition live. They get mixed into the
array class. In the same way, if we look at hashes ancestors, we have hash and it also
includes the enumerable module. So that's why array and hash both have methods like
each and select and reject and all those common methods. It's because they pick up those methods
from the same enumerable module.
So now that we understand how mixins work, let's return to our movie.
Yeah, and in the movie app, we'll create a similar rankable module that we just did
in this little example file. And then we'll mix it into both movie and I'll create a class
called song just to see that we can mix it in there too.
Perfect.
So back over in our movie class, we have these two methods, thumbs up and thumbs down, which
really has to do with ranking things. So let's extract these out into a module. I'm just
going to cut them out of our movie class here. And I'm going to create a new file. I'm going
to call that file rankable.rb. And then we'll create a module inside that file. We'll call
the module rankable. We'll put in those two methods. So anything that's rankable is going
to have the behavior of being able to thumbs up or get thumbs down. And the module just
assumes that when we mix these methods into a class, that that class has an at rank instance
variable, which our movie does. So back over to our movie now at the top, we need to make
sure we require our new rankable module. And then to mix those methods into our movie class,
we need to include rankable just like that.
Now if we go out over the command line and run our program again, we can run Ruby flix.rb
and we'll just say we want three viewings. And sure enough, things are getting thumbs
up and thumbs down just like we would expect. So we've just moved that code into our rankable
module and then mixed it in.
So let's take this a step further. If we go back over to movie, what other parts of movie
have to do with being ranked? Well, let's see. We've got this hit method and this status
method. They go together and they act on the at rank instance variable. We could also put
our normalized rank in there. And in fact, we could even put in the overloading of this
comparison operator because that has to do with rank as well. So let's just cut this
out of here. We'll go to our rankable module again. And we've got that. So the module can
thumbs up, thumbs down, determine hits and statuses, and also do sorting based on the
rank of things.
And just to make this a little bit more generic, I might change this to just other instead
of other movie because it doesn't really matter. It's anything that has a rank attribute and
a rank instance variable. So we've totally encapsulated the idea of something being rankable.
Just to make sure, I'm going to go back over to the command line. Let's run our program
one more time. Let's say we want 10 viewings. And sure enough, we're getting what we expect.
So the program still works from the outside exactly as it did before. We've just encapsulated
the notion of something being rankable.
Okay, so that works, but it's generally considered better practice if a module depends on a method
or an attribute in the hosting class, the class that it gets mixed into, as opposed
to relying on the instance variable being present. So let's change that around. We'll
just go back to rankable here. And instead of relying on this rank instance variable,
let's just assume that the class it gets mixed into has an attribute called rank. So we could
just call self.rank. That's going to be the object that we get mixed into. And we're using
the attribute there for rank. And we'll change this one to self as well.
So get rid of all the instance variable references to rank. And the same thing right down here.
So these ones where we're just accessing rank, self.rank, we're using the reader attribute
to do that. But up here where we're thumbs-upping and thumbs-downing, we're actually changing
the rank. And if we look over in our movie, our rank is still a read-only attribute. We
have attribute reader here.
So to make this work, we're going to change this to an accessor. And now we've got a reader
and a writer for rank, which means that rankable is now just dependent on any class that it
gets mixed into just has to have an accessor called rank. And that means we have a fairly
clean separation of concerns here. The rankable module just deals with rankable things. And
it doesn't care what instance variables are available in the hosting class.
So mix-ins are less coupled than inheritance. They're not an is a relationship. We can just
put code inside of modules and then mix them in wherever we want to. And we can mix in
more than one module as well. So a song might be rankable, searchable, and even danceable.
And a movie is just rankable and searchable. We can mix and match these things as we go.
And this is a preferred design in Ruby.
So when designing your software, try to identify able behaviors and encapsulate those behaviors
into modules that then you can mix into multiple classes.
So now we've looked at two design techniques for sharing common code, inheritance and mix-ins.
In the exercise, you're going to put a couple mix-ins in the game.
And when you're done, we'll have all the features of the game implemented. So in the
next section, we'll look at how to package up our code as a Ruby gem so we can distribute
it out to your friends or the rest of the world.
So come on back.
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.