When last we talked, we had a Button class that could be used to quickly create a button of a certain style. However, I also asked you to ignore certain aspects of the code. We're going to start this session off with a little resolution: it's time to explain what extends
does.
Over the course of this tutorial, we'll expand that Button class, gain a deeper understanding of what's actually going on, and learn a few more Object-Oriented Programming terms and techniques, namely inheritance. The Button class will get better and more useful.
Step 1: Inheritance
One of the powerful features of OOP is inheritance. First things first, that's inheritance in the sense of genealogy, not inheritance in the sense of receiving sums of money from a deceased uncle.
In a nutshell, inheritance enables a given class to take an existing class and do everything that it can do, but with modifications and/or additions. In other words, suppose class A
does two things, called foo
and bar.
If class B
inherits from A,
then it will automatically also do foo
and bar.
B
can also then define an extra thing it can do, called baz.
It can also choose to still do something called bar,
but change the internal behavior a bit.
A practical illustration will help explain this.
Step 2: E.g: The DisplayObject
In ActionScript 3, there is a class named DisplayObject. This class defines properties and methods relevant to any visual element: x
and y
, width
and height
, alpha
, filters
, mask
, rotation
, getBounds()
, hitTestPoint()
, etc (see the documentation for a full list). Essentially, any task appropriate for all types of displayable objects are defined here.
There are a number of classes that extend DisplayObject
. One of these is Shape. All that Shape
does is define a graphics
property, which enables programmatic vector drawing. Basically, Shape
is a displayable object that specializes in vector graphics. And because Shape
extends DisplayObject
, it inherits all of those other, more general properties and methods, and a Shape
is said to be a kind of DisplayObject
(see the documentation for the Shape
class).
Another subclass of DisplayObject
is Bitmap. Bitmap
is sort of the pixel analog of Shape
; it merely adds a few properties relevant to work with bitmaps (pixels) with ActionScript. Again, though, because it extends DisplayObject
it can also be positioned, rotated, filtered, hit-tested, etc, just like DisplayObject
, and thus Bitmap
is said to be another kind of DisplayObject
(see the documentation for the Bitmap
class).
What's more, a third class that inherits from DisplayObject
is InteractiveObject. With InteractiveObject
, there are half-a-dozen properties added pertaining to context menus, keyboard navigation, mouse enablement. The big thing InteractiveObject
adds is a whole slew of events that it is capable of dispatching, from mouse rollovers to keyboard focus to any kind of possible user input. And, once again, an InteractiveObject
is a kind of DisplayObject and so it can also be positioned, et al (documentation for the InteractiveObject
class).
The following graph shows the relationships of the various classes we just discussed (along with one we haven't: Video. Hopefully the purpose of this class is obvious).
In a graph such as this, a line with an arrowhead at one end indicates an inheritance relationship. The box with the tail of the arrow is the class that extends the box with the head of the arrow.
Step 3: The Inheritance Structure
At this point, we've looked at four classes: one superclass, and three of its subclasses. Note that while all three subclasses share some common functionality (position, rotation, etc.), all of the overlap is defined in DisplayObject
. The Shape
has a graphics
property, Bitmap
has a bitmapData
property, and InteractiveObject
dispatches a rollOver
event, and those are unique to those classes. A Bitmap
can't be programmatically drawn into, and a Shape
can't dispatch a rollOver
event.
This is a common use for inheritance: to consolidate functionality that is common to a family of classes. All of these display objects need to be positioned, so rather than putting positioning code in each of the classes, Adobe put the code in a super class from which the all other display objects can inherit.
The list goes on: InteractiveObject
has subclasses of its own. TextField is an impressive class that extends InteractiveObject
but also adds many properties and methods dealing with setting, getting, and styling text. Not only does it extend InteractiveObject
, and therefore inherits all of the events and things defined there, but it (indirectly) inherits from DisplayObject
, so it too can be positioned, rotated, etc. That functionality continues down the family tree (see documentation for the TextField
class).
Another class called DisplayObjectContainer is also a subclass of InteractiveObject
, and it adds functionality for managing other display objects that can be contained within another display object, like addChild()
and swapChildren()
(child
being the general term used for a display object that is contained by another display object) (see documentation for the DisplayObjectContainer
class). Furthermore, Sprite extends InteractiveObject
, adding dragging capabilities along with some interactivity conveniences and a graphics
object (see documentation for the Sprite
class).
The following image illustrates the entire inheritance tree as we've discussed to far.
Step 4: The MovieClip as Example
And last but not least, MovieClip extends Sprite, defining the ability to work with the timeline (with properties like currentFrame and methods like play()
and gotoAndStop()
). By the time we get to MovieClip, it has a lineage that goes up through Sprite to DisplayObjectContainer
to InteractiveObject
to DisplayObject
, thus making it capable of:
- Position, rotation, filters, and hit tests (thanks to
DisplayObject
) - Dealing with interactivity, namely dispatching events related to user input (thanks to
InteractiveObject
) - Containing other display objects (thanks to
DisplayObjectContainer
) - Working with dragging and being drawn into (thanks to
Sprite
) - Managing its timeline (thanks to
MovieClip
itself)
That's a lot of functionality, but the hierarchy of inheritance for all display objects enables a lot of code reuse, so really MovieClip isn't adding all that much to the table. It's just the next logical step for adding features (see documentation for the MovieClip
class).
It's also worth noting that DisplayObject
is itself a subclass: it extends EventDispatcher so that it's capable of dispatching events and working with the standard AS3 event model. MovieClip
has quite a heritage.
For an overview on Flash's display list, please see Adobe's documentation page on Display Programming, as well as my previous tutorial covering the display list.
The following illustration summarizes the hierarchy of inheritance that we've discussed in the previous three steps, also adding a few classes we haven't talked about:
Note that this illustration is not comprehensive; there are incomplete lists of properties and methods, and in fact there are entire classes missing. The purpose is to provide a compact representation of the extensive DisplayObject
hierarchy.
Step 5: Extending MovieClip
So, when we write this:
public class Button101 extends MovieClip
We're saying that the class we're writing will actually merely define the extra properties and methods that we'll sort of graft on the existing MovieClip
class. That is, Button101
is a MovieClip
, only with a little extra built-in capability.
In the end, that means that from within the Button101
class, we can use DisplayObject
properties and methods as if we wrote them ourselves, like we do here:
bgd = new Shape(); bgd.graphics.beginFill(0x999999, 1); bgd.graphics.drawRect(0, 0, 200, 50); addChild(bgd);
That addChild()
method is defined by DisplayObjectContainer
, but ultimately we inherit from DisplayObjectContainer
so we can use it.
Similarly, any bit of code that uses a Button101
object can basically treat it like any other display object, like we do here, in DocumentClass
:
button = new Button101(); button.x = 10; button.y = 200; addChild(button);
So, when we create the Button101
object, we can think of it as just a specialized MovieClip
. It's not unlike how you create specialized MovieClips
in the Flash IDE and store the symbols in the library. The only real difference is that in that case, you use the graphical tools instead of writing a bunch of code.
In fact, the double whammy here is that DocumentClass
itself extends MovieClip
, and so it too inherits all of these methods and properties, which is how we have access to an addChild()
method in the first place.
(Note that you can combine those two approaches and write some custom code that is linked to a symbol in the library, but we won't address that technique in this tutorial.)
Step 6: Now What?
OK, that was quite a bit of theory, so take a quick breather if you need it. The point is to come to grips with the terminology and techniques, but not necessarily master it in one go. Object-Oriented Programming is a significant mental leap, so don't expect it all to sink in right away.
In fact, if you need to, just stop reading right now and come back to this after your head stops spinning. Or even re-read the previous several steps to make sure you've got it.
Even if you're ready to move on right now, the sole purpose of this step is to not think so much...
Carry on when you're ready.
Step 7: Mapping a Button to an Action
Right now, our two buttons both have their CLICK
events handled by the same method in the document class. Sometimes it's useful to have two buttons do the same thing to give the user a few choices, but most often different buttons do different things. How can we set up multiple actions for multiple buttons?
There are a few approaches to this problem, and I'll outline a few.
Step 8: Option 1 Multiple Event Handlers
First, and probably the most obvious, is to simply set up a second event handling method. For instance (code abbreviated to focus on the relevant bits...feel free to integrate into your working project to test it out, but for the purposes of continuing tutorial this isn't officially code to enter into the document class):
button.addEventListener(MouseEvent.CLICK, onButton1Click); button2.addEventListener(MouseEvent.CLICK, onButton2Click); // ... private function onButton1Click(e:MouseEvent):void { trace("Button 1 was clicked"); } private function onButton2Click(e:MouseEvent):void { trace("Button 2 was clicked"); }
This is probably the most common solution to this situation, especially when two buttons perform rather disparate functions. When one button opens an informational overlay, and another purchases a product, it makes sense to do what probably comes naturally and make separate functions.
Step 9: Option 2 Switch/Case
However, when buttons perform very similar functions, such as multiple product thumbnails needing to open up a product detail, just with different content, then making multiple handlers can be tedious at best, and can lead to hard-to-manage code at worst. In this case, it's probably be better to utilize a conditional statement or a Dictionary (see AS3 101 - Branching for more in-depth discussions of both of these techniques).
button.addEventListener(MouseEvent.CLICK, onButtonClick); button2.addEventListener(MouseEvent.CLICK, onButtonClick); // ... private function onButtonClick(e:MouseEvent):void { switch (e.target) { case button: trace("Button 1 was clicked"); break; case button2: trace("Button 2 was clicked"); break; } }
If the thumbnails were dynamically generated (using external data and a loop...see AS3 101 - Loops and AS3 101 - XML), that makes it darn near impossible to create a unique event handler for each button, and even makes it pretty hard to pull off the above trick (although a Dictionary
would work well).
Step 10: Option 3 Objects That Store Data
However, there is another option that starts to become attractive as OOP techniques are utilized. It's a step further than the last option; instead of comparing the event target to a known list of possible targets, we simply ask the target for the relevant piece of information.
Without any further modification to the Button101
class, we can illustrate this rather simply. Suppose in our trace message, it was our goal to output the x
position of the button that was clicked, something like this:
The button that was clicked has an x value of 10
We can do that very easily by simply referencing the x
property of the target
itself.
private function onButtonClick(e:MouseEvent):void { trace("The button that was clicked has an x value of " + e.target.x); }
Check that out, one line! No need for dictionaries, switch
statements or multiple, near-identical event handlers.
Remember that excessively long discussion about inheritance? Of course you do. Because e.target
is a Button101
object, and because a Button101
object is ultimately a DisplayObject
, we have an x
property, even though we never defined that property in our Button101
class.
This becomes even more powerful when we realize we can create custom properties on the Button101
class (or any custom class) in which its objects can store data. Suppose that, for instance, instead of the x
value of the button, we wanted to navigate to one URL or another. And if each button instance had its own URL data stored in a url
property, we can handle the execution of the click very easily.
If you're thinking that this could be as simple as:
private function onButtonClick(e:MouseEvent):void { navigateToURL(new URLRequest(e.target.url)); }
Then you're correct. However, to get that url
property to exist in the first place, we have some work to do in the Button101
class. And before we do that, we need to talk about access modifiers.
Step 11: Public Access
The time has come to address these public
and private
words I've been telling you to ignore (finally!). These are called access modifiers and define how a property or method can be accessed (or not) from other parts of the program.
There are four modifiers:
- private
- protected
- internal
- public
The order of that list is intentional; the amount of access increases as we go down the list. Here's a quick run down of the access types:
private is the least accessible. The only object that will be able to access a private property or method is the one that owns it. For example, the two properties of Button101
, label
and bgd
, are private
, and so the only object that can work with those items is the Button101
instance itself. Note than even two instances of the same class have mutually exclusive access to their own private properties; button
can access its label
property, but it can't access button2
's label
property, and vice versa.
protected is very similar to private
, only that it allows any subclass to also access the property or method. So if we created a subclass of Button101
, that subclass would not be able to access the label
or bgd
properties (because they are private), but could access another property that was defined as protected.
internal expands the accessibility a little more. It grants access to any other object that is in the same package
as the class. This one is used far less that the others and only really makes sense once you understand packages, so we won't be revisiting this one. Note, however, that internal
is the default access if you forget to specify (don't ask me why).
public is the most accessible. Basically, any object can access the property or method. If another object has access to a Button101
object, then it can call the setLabel()
method.
ActionScript 3 will enforce these rules. If you make a property private and then try to access it from a subclass, then the compiler will complain and you'll get an error message. The idea is that you set the rules down as you craft your class ("this should be accessible, that shouldn't") and then you need to stick to them.
Step 12: Make a private _url property
In Button101, declare a third property, _url
, to hold a URL string.
private var _url:String;
What's up with the underscore? This is a convention that I've been glossing over until we've had a discussion on access modifiers. It's not required, but it's quite common to declare your private and protected properties with names that begin with underscores. Part of this has to do with convention, part to do with being able to recognize at a glance an object property versus a local function variable (remember scope?), and a lot to do with the topic of the next step.
I would advise you to read up on this Quick Tip on how to choose an access modifier for your properties and methods. In a nutshell, though, the main takeaway is to always declare properties as private
or possibly protected
, so as to make sure they don't get changed without the object's approval. However, we still need a way to access the data, and this is where setters and getters come in.
Step 13: Setters and Getters
OK, so you have a private property, and you'd like to make it public. You could just change the "private
" to "public
" and be done with it, except you recall some crotchety tutorial author telling you to never do that. What do you do? You use setters and/or getters.
Setters and getters are, simply put, methods that either set the value of some property, or get the value of some property. There are two kinds of setter/getter methods to keep in mind: implicit and explicit.
Explicit setters/getters are what you're probably already thinking they are: regular old methods. Given a private property "_url
" of type String
, a setter might look like this:
public function setUrl(theUrl:String):void { _url = theUrl; }
And a getter might look like this:
public function getUrl():String { return _url; }
Note that the setter receives a single parameter of the same type as the property, simply sets the property's value to that incoming value, and returns nothing. The property is set, and that's it.
Similarly, the getter does a single thing: it returns the value held by the property, and takes no arguments.
To use these, you'd simply call them like any other method. If we had an instance of this object in the variable button
we could do this:
button.setUrl("http://active.tutsplus.com/"); trace(button.getUrl()); //traces 'http://active.tutsplus.com'
And that should not be a surprise. So why am I going into so much detail on this topic? Because of the implicit setters/getters.
Most OOP languages afford this capability, and ActionScript 3 does so with the set and get keywords. These are extra modifiers you place in your method declaration that makes these method implicit setters and getters. We'll get to what that means in a second, but for now, add this setter to the Button101
class:
public function set url(theUrl:String):void { _url = theUrl; }
Notice it looks pretty darn close to the previous setter, only the word "set
" is out there by itself, between the word "function
" and the name of the function. This is a subtle detail; the word "set
" is not part of the function name. It's an extra keyword that turns a method into an implicit setter. Otherwise, though, it's purpose should be obvious.
Next, add this getter:
public function get url():String { return _url; }
Again, it's almost identical to the previous getter, only with the "get
" keyword in between the "function
" keyword and the function name (if you added the previous explicit examples, please remove them to prevent confusion).
Now, here's where things get interesting. Back in the DocumentClass, let's set the url, and then we'll trace it right away to (a) make sure it worked, and (b) see what using an implicit getter looks like.
At the end of the constructor, after both buttons have been created, add the following lines:
button.url = "http://active.tutsplus.com/"; button2.url = "http://www.google.com/search?q=actionscript+3+tutorials"; trace(button.url); trace(button2.url);
That's a significant difference; it looks like you're working with a property, not a method! That's the magic of implicit setters and getters. They take a method and make it look like a property. Working with it is just like setting or getting the property, but inside the class we have a function.
Now, why go to this trouble? There are, naturally, a few compelling reasons, a few of which we'll explore in the next few steps as we add more setters and getters.
Step 14: Get the URL
Let's go back to the original problem of knowing what to do when a button is clicked. Earlier, I mentioned that could go in the document class; let's make that a reality.
In DocumentClass.as, update your onButtonClick() method to look like this:
private function onButtonClick(e:MouseEvent):void { navigateToURL(new URLRequest(e.target.url)); }
And in the constructor, make sure the url properties are set on both buttons, but go ahead and remove the traces:
button.url = "http://active.tutsplus.com/"; button2.url = "http://www.google.com/search?q=actionscript+3+tutorials"; //trace(button.url); //trace(button2.url);
Finally, you'll need to add the following import line at the beginning:
import flash.net.*;
For reference, the complete DocumentClass
should look like this:
package { import flash.display. MovieClip; import flash.text.TextField; import flash.events.MouseEvent; import flash.net.*; public class DocumentClass extends MovieClip { private var tf:TextField; private var button:Button101; private var button2:Button101; public function DocumentClass() { tf = new TextField(); addChild(tf); tf.text = "Hello World"; button = new Button101(); button.x = 10; button.y = 200; button.setLabel("Button 1"); button.url = "http://active.tutsplus.com/"; addChild(button); button.addEventListener(MouseEvent.CLICK, onButtonClick); button2 = new Button101(); button2.x = 220; button2.y = 200; button2.setLabel("Button 2"); button2.url = "http://www.google.com/search?q=actionscript+3+tutorials"; addChild(button2); button2.addEventListener(MouseEvent.CLICK, onButtonClick); } private function onButtonClick(e:MouseEvent):void { navigateToURL(new URLRequest(e.target.url)); } } }
And just to be sure, here is the complete Button101
class:
package { import flash.display.Shape; import flash.display.Sprite; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFormat; public class Button101 extends Sprite { private var bgd:Shape; private var labelField:TextField; private var _url:String; public function Button101() { bgd = new Shape(); bgd.graphics.beginFill(0x999999, 1); bgd.graphics.drawRect(0, 0, 200, 50); addChild(bgd); labelField = new TextField(); labelField.width = 200; labelField.height = 30; labelField.y = 15; var format:TextFormat = new TextFormat(); format.align = "center"; format.size = 14; format.font = "Verdana"; labelField.defaultTextFormat = format; addChild(labelField); addEventListener(MouseEvent.ROLL_OVER, onOver); addEventListener(MouseEvent.ROLL_OUT, onOut); mouseChildren = false; buttonMode = true; } public function setLabel(label:String):void { labelField.text = label; } private function onOver(e:MouseEvent):void { bgd.alpha = 0.8; } private function onOut(e:MouseEvent):void { bgd.alpha = 1; } public function set url(theUrl:String):void { if (theUrl.indexOf("http://") == -1) { theUrl = "http://" + theUrl; } _url = theUrl; } public function get url():String { return _url; } } }
Go ahead and try it out. Clicking on the buttons will take you to one of two web pages.
Step 15: Setting and Getting the Label
Another reason to roll with setters and getters is the ability to make a complex task simple to execute. For example, let's add the ability to update the label using a setter. We actually already did this in the previous tutorial, but we didn't use the term setter. We'll update the existing setter to be implicit and add a getter.
In Button101, you'll see this method:
public function setLabel(label:String):void { labelField.text = label; }
Remove that, and add this:
public function set label(text:String):void { labelField.text = text; } public function get label():String { return labelField.text; }
This lets us, from elsewhere in the program, work with a "label property" that simply sets the label text of the button. What's really going on behind the scenes, though, is a little more complicated; you need to work with a TextField
and a String
, not just a String
.
This is encapsulation again. You might think, "why not just make the labelField
property public -- or, OK, make a setter and getter for the labelField
-- and then do something like button.labelField.text = "Button 1"
?"
That would certainly work, but do you really need to expose the labelField
to the rest of the world? Assuming that the general style of the button is set in stone, there's really no need to work with that TextField
outside of the Button101
class, except to set the label text. Exposing the labelField
would allow another object to not only set the text, but to also position, rotate, style, or even hide the label. This is probably not desirable; even if you're a good citizen and wouldn't intentionally write code that does that, it's better to protect yourself from accidental changes. Let the compiler warn you if tried a stunt like that, as opposed to letting the application run and having your label mysteriously rotated.
In DocumentClass, make sure to change the lines that call setLabel
to label =
:
button.label = "Active Tuts"; button2.label = "Search";
Step 16: Adjusting the Width and Height with Overrides
We'll examine another argument in favor of the setter. We might want to adjust the width or height of the button. Since the button class extends MovieClip
, it already has width
and height
properties, which is handy, except it's not quite what we want. Check out what happens if we set it:
The above SWF was the result of setting the height
property of the left-hand button object to 100
. If you want to try this yourself, just add the following line at the end of the constructor of your DocumentClass
:
button.height = 100;
Now, this isn't terrible, but I would argue that it's undesirable. It's happening because this is the default behavior for scaling MovieClips
(and all DisplayObjects
, actually, since height
is defined in DisplayObject
). It's merely a linear stretching of the artwork contained within, text fields included. The text at least is not distorting, but its size is disconcerting.
We can write our own method to set the width, such as public function setWidth()
, but there's an even better way. We can override the behavior of a method defined in a superclass. We can, as a subclass, actually fine-tune the functionality to our needs by changing the definition of what an existing method does. It looks like this:
override public function set width(w:Number):void { // Custom code here. } override public function set height(h:Number):void { // Custom code here. }
It's a standard method declaration, except with an extra keyword at the front: override
. Note that the order of "public
" and "override
" is actually interchangeable, but most code I've seen tends to put the "override
" word first. It doesn't matter, but it's advisable to pick a standard for yourself (or your company) and stick with it.
Let's add some functionality. Keep in mind that we are completely replacing the existing set width
method, so we need to make sure we cover all the bases. As it turns out, it's fairly simple. Instead of stretching the main MovieClip
container, and all of the children within it by default, we'll actually stretch each of the children individually. It's almost strange that it works, but it does:
override public function set width(w:Number):void { labelField.width = w; bgd.width = w; } override public function set height(h:Number):void { labelField.height = h; labelField.y = (h - labelField.textHeight) / 2 - 3 bgd.height = h; }
By setting the width and height of the text field explicitly, we avoid the awkward text scaling. We can also add in a line of logic to reposition the label
TextField
so that it remains vertically centered.
Note that we are not writing a getter; the behavior defined by DisplayObject
is just fine, and there is no need to override it.
To try it out, simply add (or keep) the line that sets the button's height to 100 pixels in the DocumentClass
.
There are a number of other reasons why you'd want to start using setters and getters. I've listed five of them here.
Step 17: Adjusting the Width Automatically
One last item on the setter technique. Here's the goal: to make the button automatically expand in width if the label is too long. So, if you set the label to "Button 1", there is plenty of space, and the text is nicely centered. But if you were to set the label to "Press This Button To Hack the Gibson," that would be too long, and the text would get truncated.
This sort of behavior is quite easy to manage with the use of a setter. When setting the label text, you can also do a little post-processing on the text field and background to make sure they are sized appropriately. Rework your label setter so that it looks like this:
public function set label(text:String):void { labelField.text = text; var autoWidth:Number = Math.max(200, labelField.textWidth + 40); this.width = autoWidth; }
The logic is relatively straightforward, but here's a rundown in case you haven't done this sort of thing before. First, we figure out what the width should be. By using the Math.max()
function, we can pass in two numbers and get the higher one back. One of those numbers is 200
, which I'm defining as our minimum width. The other number is the width of the text in the text field, plus a 40 pixel margin (20 pixels on either side). So if the text width is small, we'll get 200
back and even with a short label we'll have a 200-pixel-wide button. If the text width is large, however, we'll get the text width (plus a margin) back, and then we'll use that value instead.
We finally simply set the width of the button to this value, calling the setter we just wrote in the last step. Try it out by setting the label of the second button to an unusually long bit of text:
button2.label = "Press This Button To Hack the Gibson";
Of course, the actual logic you take here could be any number of things. You might find it more useful to implement a "minimumWidth
" property that specifies the minimum number of pixels (instead of 200
), or an "autoFit
" property that signals whether or not to adjust the width automatically, to make the Button101
a little more flexible. I leave these options to the reader as further exercises.
But those options bring up a good point. Adding the necessary code to implement such features will start to make your class more complex. That in itself isn't really a bad thing, but it does herald the truth that the more features you have, the more complex your code. One of the hallmarks of good Object-Oriented Programming is that complexity lies in the ways in which several objects are connected together, not in the logic of a single class. Some classes will be, by necessity, rather complex, but as a rule, more and smaller classes are preferable over fewer and larger classes.
Take the aforementioned DisplayObject
hierarchy as an example. The MovieClip
class (or Button101
class, for that matter) is in itself rather complex, given that it can dispatch events, position itself, handle interactivity, contain other DisplayObjects, be dragged, and manage a timeline, but remember that a vast majority of that complexity is defined by classes other than MovieClip
. Remember, everything other than the timeline-related stuff is defined in a super class. The DisplayObject
hierarchy is a rather well-thought out system of classes, each one taking on the responsibility of a focused task.
This is a point that's difficult to execute well, so I just want to mention it, plant that seed, and hopefully seep into your sub-conscience for a more opportune time.
Summary
In this installment, we took a long hard look at primarily two things: inheritance; and setters and getters.
Inheritance is the mechanism by which one class can leverage most everything already defined in another class, but sub-classing it. The subclass can then add more functionality or even redefine existing functionality via overrides.
Setters and getters are any method than can be used to access a private property. Implicit setters and getters look like properties themselves, but are really special methods. They can be useful for controlling access and performing validation, or executing more complex logic behind the simple setting of a property.
There is still much to learn on this topic, so stick around for the forthcoming Part 3. You're doing great, and thank you for sticking it out this far!
Comments