[music] The question of when it is good style to use subclassing is an important
one, and you could have an entire course on object oriented design.
But here, I just want to answer the question for our simple example of points
and color points by considering some alternatives that we could have pursued
instead. So, remember what we did is we had a Point
class that had several methods to find, like getters and setters for x and y, as
well as from distance for the origin methods and we wanted a different class
that was very similar. That was a ColorPoint class that had all
of that, but also had a getter and setter for a color property.
And then, we had to change our initialize method to make sure that, that color was
properly initialized. So, it turns out here, subclassing worked
very well, it was convenient to reuse all that code from, from Point.
But in general, once programmers learn subclassing, they do often tend to overuse
it. All right.
Most people would agree that in large OOP applications, there tends to be more
subclassing than there probably should be. So, let me show you what else you could do
rather than subclassing in order to get code reuse, even though, in this case, as
we'll see sub classing is actually perfectly good style.
So, here's the first alternative, since we're in a dynamic language where we can
change class definitions anywhere in our program.
What if we, instead of creating a ColorPoint class, just went and added new
things to the Point class. So, go into the point class, we know we
can do this anywhere in our program. Add the color and color equal getter and
setter methods. Change the initialized method, and by
providing a default argument for the color field, all the old uses of the initialize
method. All the old calls to new will continue to
work. We'll just be creating a new instance
variable that people didn't know was there before.
So, you can do this. You can get away with this in Ruby.
It's, it's often bad style if some other library wrote the Point class, you could
be messing up all sorts of invariance by going in and changing it.
You are now requiring every point in your system to have these color properties,
even if many of them don't need it, and it's kind of a non-modular change.
If you add a color field and someone else adds a z-coordinate and someone else adds
a string, which is the name of the Point. You're just bloating your objects with all
these different things, and that's if they're separate.
If they interact in any way, if any of the methods assume the other ones don't exist,
then you get into conflicts and, and all sorts of strange things should happen.
So, this will work, particularly for these sort of simple examples, but it's not a
particularly modular change. If we want to, in any way, keep the idea
of a ColorPoint separate from the idea of a Point.
So, here's a second alternative. Instead of subclassing Point, we could
just copy and paste the methods. Now, we wouldn't be relying on Point at
all, so things would be very separate in modular.
We would just define a class ColorPoint. And since it's such a short class, there
isn't too much to it. You, you know it almost fits on the slide.
I, I omitted the details of the initialized method here and we would just
say a ColorPoint has 3 getters and 3 setters for x, y, and color.
It has an initialize, it has this distFromOrigin method, which I literally
copy and pasted from the Point class, and distFromOrigin2.
Now, what's good about this is that now points and color points are completely
separate. Any changes anyone makes to the Point
class will have absolutely no effect on how any instance of ColorPoint.
And the obvious disadvantage is that I copy and paste the code.
So, I'm not getting any code reuse. If there was some bug in distFromOrigin,
I've just duplicated that bug. If someone adds good new functionality to
Point, I would have to go in and copy that over here.
Otherwise, I wouldn't be able to use it. So, this is a typical trade-off of, do I
want to re-use code or, do I want to make a copy and we generally prefer code
re-use? Here's the third alternative and this is
the one that people in objected oriented languages tend do to little of which is
instead of subclassing Point. What if instances of ColorPoint had inside
of them an instance of Point, in an instance variable as part of their private
state? So, here's how this would work.
I would just define a class ColorPoint, the super class would just be an implicit
object, I'm not going to use anything from the super class explicitly here.
I would have color getters and setters. But then, the other instance variable I
would have is pt here, short for Point. And I will initialize it here in the
initialized method to hold a Point. So now, my methods x, x equal, y, y equal.
Distfromorigin, distFromOrigin2 will not work as they did before, but I could just
forward the message, just make a nested method call to the underlying Point.
So, when someone had an instance of ColorPoint and they called the x method,
that would be this code that you see here. That would just, the body would just send
the same message, would just call the x method on the underlying Point.
Now, the good thing about this is that it encapsulates inside of ColorPoint the fact
that there's a point. It's an implementation detail.
It would let us change certain things. For example, if in ColorPoint, we wanted
the x and y coordinate to be called fu and bar, then we would just change the names
of these methods, and it wouldn't matter that the methods were called something
different than the Point class. The disadvantages are two-fold.
First of all, it's not nearly as convenient to code reuse.
All right, I have to create all these methods just to pass a method to some
underlying thing. But, much more interesting is that now a
ColorPoint really is not a Point. There is a method that we saw in Ruby
actually agrees with me. It's this totally separate thing whose
internal representation happens to include an instance of Point.
So, this is an even bigger deal in languages with static type systems where
it matters to us whether an instance of ColorPoint can have some type that
instances of Point also have. And here they would, they would not.
It's less of a big deal in a dynamically type language like Ruby.
But, if you see code like this for something like ColorPoint it is actually
inferior style, subclassing works really well here.
Subclass says, I really do want to inherit all the methods from Point, I'm just like
a point except I have some new things and I need to change the definition of the
initialized method. And that's why even this third alternative
for color points is an inferior one. But as I mentioned before, often times in
Ruby programs and in other object-oriented programming languages, you should be doing
something like this. But sort of out of laziness or convenience
or sloppy thinking, you end up using subclassing instead.