Would you like to inspect the original subtitles? These are the user uploaded subtitles that are being translated:
1
00:00:00,000 --> 00:00:07,000
Now in the last section we looked at how inheritance can be used to share code that's common between
2
00:00:07,600 --> 00:00:13,880
classes. And this works well with classes that have a natural inheritance relationship.
3
00:00:13,880 --> 00:00:20,880
For example, your clumsy player is a kind of player and our 3D movie is a kind of movie.
4
00:00:20,960 --> 00:00:25,620
Right, but sometimes we have code that we want to share across classes that don't have
5
00:00:25,620 --> 00:00:31,360
an is a relationship. And Ruby has a wonderful alternative to inheritance called mixins.
6
00:00:31,360 --> 00:00:38,200
A mixin is simply a module that has methods and those methods get mixed into other classes.
7
00:00:38,200 --> 00:00:43,560
So for example, imagine a scenario where we want to model the concept of a song. We would
8
00:00:43,560 --> 00:00:48,640
create a song class. And a song should be rankable, just like a movie. We can thumbs
9
00:00:48,640 --> 00:00:54,000
up it or thumbs down it. And currently all that behavior lies in the movie class.
10
00:00:54,000 --> 00:00:58,720
But we don't want to copy all that behavior into the song class. We'd end up with duplication.
11
00:00:58,720 --> 00:01:03,320
And it doesn't make sense for a song to inherit from a movie. It doesn't pass the is a rule.
12
00:01:03,320 --> 00:01:05,640
A song is not a kind of movie.
13
00:01:05,640 --> 00:01:10,480
Instead we want to put all the rank related code in a module.
14
00:01:10,480 --> 00:01:14,240
So let's say we have these two classes, a movie class and a song class. And suppose
15
00:01:14,240 --> 00:01:19,280
we want to share some common behavior across these classes which aren't in the same inheritance
16
00:01:19,280 --> 00:01:23,960
hierarchy. For example, to keep it simple, suppose we want to be able to call a thumbs
17
00:01:23,960 --> 00:01:28,600
up method and print out their name. Well, we could write a thumbs up method inside of
18
00:01:28,600 --> 00:01:32,800
each of these classes, but then we'd end up with duplication. So instead we can write
19
00:01:32,800 --> 00:01:38,560
a module. We'll call the module rankable, like that. And then inside of the module we'll
20
00:01:38,560 --> 00:01:41,920
define that thumbs up method.
21
00:01:41,920 --> 00:01:45,320
Inside of the thumbs up method, all we're going to do for now, just to keep it simple,
22
00:01:45,320 --> 00:01:51,440
is we're going to print out the title, Got a Thumbs Up. Just like that.
23
00:01:51,440 --> 00:01:55,280
Now a couple things to note here. This is a regular instance method. Remember when we
24
00:01:55,280 --> 00:01:59,960
defined methods before in modules, we would put self dot and then we would call it on
25
00:01:59,960 --> 00:02:05,020
the module. Well in this case, we're defining an instance method, thumbs up.
26
00:02:05,020 --> 00:02:09,160
Now we saw before that we can't create an instance of the rankable module. Therefore
27
00:02:09,160 --> 00:02:15,120
we can't call this instance method directly. The way that we call it is we mix it into
28
00:02:15,120 --> 00:02:21,640
these classes below. So down in movie, to mix in this module rankable, I say include
29
00:02:21,640 --> 00:02:29,200
rankable and then down in song, I would do the same thing, include rankable like that.
30
00:02:29,200 --> 00:02:35,520
Now inside of movie, or for a movie object, I can call it movie dot thumbs up and in the
31
00:02:35,520 --> 00:02:41,720
same way I can take a song and call thumbs up on it. If we run that, sure enough, Goonies
32
00:02:41,720 --> 00:02:46,640
got a thumbs up and Ruby Baby got a thumbs up. So they both have a thumbs up method that
33
00:02:46,640 --> 00:02:51,820
came from this module. Something else to notice is the module or the instance method here,
34
00:02:51,820 --> 00:02:57,120
we can use at title because the point at which this method gets run will be when it's down
35
00:02:57,120 --> 00:03:02,140
inside of a movie and a movie has a title instance variable and so does a song.
36
00:03:02,140 --> 00:03:07,340
So something kind of cool about this is mixed in modules effectively behave like super classes.
37
00:03:07,340 --> 00:03:12,460
In other words, it changes the method lookup hierarchy. The module lives between the classes
38
00:03:12,460 --> 00:03:17,240
that include the module and their direct super class. So when we call a method, it looks
39
00:03:17,240 --> 00:03:22,080
in the class, then it looks in the module, then it looks in the super class and then
40
00:03:22,080 --> 00:03:27,780
all the way to the top. We can look at this another way by loading up our mixins file
41
00:03:27,780 --> 00:03:32,220
where we've got our song on our movie there and it printed out some code that we have,
42
00:03:32,220 --> 00:03:39,940
some example code. But if we look at movies.movie.ancestors now, we've got movie, but then notice that
43
00:03:39,940 --> 00:03:44,860
the module name rankable is between movie and the object. So Ruby is going to look here
44
00:03:44,860 --> 00:03:51,180
for methods, then at rankable, then up to object. So it's inserted the module into the
45
00:03:51,180 --> 00:03:56,260
lookup hierarchy here. In the same way, if we look at songs ancestors, rankable is between
46
00:03:56,260 --> 00:04:01,940
it and object. And something kind of interesting, if we look at something like arrays ancestors,
47
00:04:01,940 --> 00:04:07,060
we have the array class, but we have this module in here called enumerable. And that's
48
00:04:07,060 --> 00:04:12,820
where all those methods like select, reject, and partition live. They get mixed into the
49
00:04:12,820 --> 00:04:19,019
array class. In the same way, if we look at hashes ancestors, we have hash and it also
50
00:04:19,019 --> 00:04:25,040
includes the enumerable module. So that's why array and hash both have methods like
51
00:04:25,040 --> 00:04:31,540
each and select and reject and all those common methods. It's because they pick up those methods
52
00:04:31,540 --> 00:04:34,700
from the same enumerable module.
53
00:04:34,700 --> 00:04:38,340
So now that we understand how mixins work, let's return to our movie.
54
00:04:38,340 --> 00:04:41,580
Yeah, and in the movie app, we'll create a similar rankable module that we just did
55
00:04:41,580 --> 00:04:45,880
in this little example file. And then we'll mix it into both movie and I'll create a class
56
00:04:45,880 --> 00:04:48,620
called song just to see that we can mix it in there too.
57
00:04:48,620 --> 00:04:49,620
Perfect.
58
00:04:49,620 --> 00:04:53,520
So back over in our movie class, we have these two methods, thumbs up and thumbs down, which
59
00:04:53,520 --> 00:04:58,380
really has to do with ranking things. So let's extract these out into a module. I'm just
60
00:04:58,380 --> 00:05:03,060
going to cut them out of our movie class here. And I'm going to create a new file. I'm going
61
00:05:03,060 --> 00:05:09,740
to call that file rankable.rb. And then we'll create a module inside that file. We'll call
62
00:05:09,740 --> 00:05:15,020
the module rankable. We'll put in those two methods. So anything that's rankable is going
63
00:05:15,020 --> 00:05:19,280
to have the behavior of being able to thumbs up or get thumbs down. And the module just
64
00:05:19,280 --> 00:05:24,380
assumes that when we mix these methods into a class, that that class has an at rank instance
65
00:05:24,380 --> 00:05:29,060
variable, which our movie does. So back over to our movie now at the top, we need to make
66
00:05:29,060 --> 00:05:35,780
sure we require our new rankable module. And then to mix those methods into our movie class,
67
00:05:35,780 --> 00:05:39,719
we need to include rankable just like that.
68
00:05:39,719 --> 00:05:46,460
Now if we go out over the command line and run our program again, we can run Ruby flix.rb
69
00:05:46,460 --> 00:05:50,300
and we'll just say we want three viewings. And sure enough, things are getting thumbs
70
00:05:50,300 --> 00:05:55,980
up and thumbs down just like we would expect. So we've just moved that code into our rankable
71
00:05:55,980 --> 00:05:58,100
module and then mixed it in.
72
00:05:58,100 --> 00:06:02,540
So let's take this a step further. If we go back over to movie, what other parts of movie
73
00:06:02,540 --> 00:06:07,500
have to do with being ranked? Well, let's see. We've got this hit method and this status
74
00:06:07,500 --> 00:06:12,940
method. They go together and they act on the at rank instance variable. We could also put
75
00:06:12,940 --> 00:06:17,980
our normalized rank in there. And in fact, we could even put in the overloading of this
76
00:06:17,980 --> 00:06:22,300
comparison operator because that has to do with rank as well. So let's just cut this
77
00:06:22,300 --> 00:06:29,980
out of here. We'll go to our rankable module again. And we've got that. So the module can
78
00:06:29,980 --> 00:06:35,660
thumbs up, thumbs down, determine hits and statuses, and also do sorting based on the
79
00:06:35,660 --> 00:06:36,660
rank of things.
80
00:06:36,660 --> 00:06:40,260
And just to make this a little bit more generic, I might change this to just other instead
81
00:06:40,260 --> 00:06:45,060
of other movie because it doesn't really matter. It's anything that has a rank attribute and
82
00:06:45,060 --> 00:06:50,740
a rank instance variable. So we've totally encapsulated the idea of something being rankable.
83
00:06:50,740 --> 00:06:53,540
Just to make sure, I'm going to go back over to the command line. Let's run our program
84
00:06:53,540 --> 00:06:58,580
one more time. Let's say we want 10 viewings. And sure enough, we're getting what we expect.
85
00:06:58,580 --> 00:07:03,900
So the program still works from the outside exactly as it did before. We've just encapsulated
86
00:07:03,900 --> 00:07:05,940
the notion of something being rankable.
87
00:07:05,940 --> 00:07:11,860
Okay, so that works, but it's generally considered better practice if a module depends on a method
88
00:07:11,860 --> 00:07:16,620
or an attribute in the hosting class, the class that it gets mixed into, as opposed
89
00:07:16,620 --> 00:07:21,860
to relying on the instance variable being present. So let's change that around. We'll
90
00:07:21,860 --> 00:07:26,380
just go back to rankable here. And instead of relying on this rank instance variable,
91
00:07:26,380 --> 00:07:31,620
let's just assume that the class it gets mixed into has an attribute called rank. So we could
92
00:07:31,620 --> 00:07:36,860
just call self.rank. That's going to be the object that we get mixed into. And we're using
93
00:07:36,860 --> 00:07:41,380
the attribute there for rank. And we'll change this one to self as well.
94
00:07:41,380 --> 00:07:49,540
So get rid of all the instance variable references to rank. And the same thing right down here.
95
00:07:49,540 --> 00:07:54,300
So these ones where we're just accessing rank, self.rank, we're using the reader attribute
96
00:07:54,300 --> 00:07:59,300
to do that. But up here where we're thumbs-upping and thumbs-downing, we're actually changing
97
00:07:59,300 --> 00:08:05,460
the rank. And if we look over in our movie, our rank is still a read-only attribute. We
98
00:08:05,460 --> 00:08:06,659
have attribute reader here.
99
00:08:06,660 --> 00:08:11,980
So to make this work, we're going to change this to an accessor. And now we've got a reader
100
00:08:11,980 --> 00:08:18,660
and a writer for rank, which means that rankable is now just dependent on any class that it
101
00:08:18,660 --> 00:08:25,100
gets mixed into just has to have an accessor called rank. And that means we have a fairly
102
00:08:25,100 --> 00:08:31,060
clean separation of concerns here. The rankable module just deals with rankable things. And
103
00:08:31,060 --> 00:08:35,960
it doesn't care what instance variables are available in the hosting class.
104
00:08:35,960 --> 00:08:40,500
So mix-ins are less coupled than inheritance. They're not an is a relationship. We can just
105
00:08:40,500 --> 00:08:45,300
put code inside of modules and then mix them in wherever we want to. And we can mix in
106
00:08:45,300 --> 00:08:50,960
more than one module as well. So a song might be rankable, searchable, and even danceable.
107
00:08:50,960 --> 00:08:55,740
And a movie is just rankable and searchable. We can mix and match these things as we go.
108
00:08:55,740 --> 00:08:58,460
And this is a preferred design in Ruby.
109
00:08:58,460 --> 00:09:03,580
So when designing your software, try to identify able behaviors and encapsulate those behaviors
110
00:09:03,580 --> 00:09:08,580
into modules that then you can mix into multiple classes.
111
00:09:08,580 --> 00:09:13,820
So now we've looked at two design techniques for sharing common code, inheritance and mix-ins.
112
00:09:13,820 --> 00:09:17,180
In the exercise, you're going to put a couple mix-ins in the game.
113
00:09:17,180 --> 00:09:20,460
And when you're done, we'll have all the features of the game implemented. So in the
114
00:09:20,460 --> 00:09:24,980
next section, we'll look at how to package up our code as a Ruby gem so we can distribute
115
00:09:24,980 --> 00:09:27,380
it out to your friends or the rest of the world.
116
00:09:27,380 --> 00:09:34,380
So come on back.
13580
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.