Journal FortKnox's Journal: OOP 102: Encapsulation 10
OK, lets see our class that we have so far (from 101):
class TV
{
int channel;
int volume;
Display picture;
void changeChannel( int newChannel )
{
channel = newChannel;
}
}
Now, when we make a class, we have a purpose for it. This is supposed to be stuff for a TV, but we are allowing the user access to all the functions AND all the variables. That is a problem. If my TV only has channels 1-99, what happens if I write this line:
tvObject.channel = 284;
It is a runtime error. I'm directly accessing the variable and setting it myself. Well, when classes were developed, this problem was addressed. The class designer is given the ability to assign access to each item within a class, so that the class user will be forced to use the class in the way it was intended. This is known as encapsulation. There are three levels of access:
private: Any item (variable or function) assigned a private access can ONLY be used from within the class itself. If we call the channel variable private like so:
...
private int channel;
...
Then the user cannot set the channel outside the class:
void main()
{
TV tvObject = new TV();
tvObject.channel = 283; //compile time error
}
But, since the changeChannel() function is within the class, it CAN use the channel variable.
public: this access allows anyone, anywhere to use the item in the class.
protected: this access allows only the class, itself to access the item (like private), but it also allows children of the class to use the item (don't worry about this one until next lesson).
As a rule of thumb, all variables should have a private access, and all functions should have a public access.
Lets rewrite our class:
class TV
{
private int channel;
private int volume;
private Display picture;
public void changeChannel( int newChannel )
{
channel = newChannel;
}
}
Now, we have limited access too much in our class. We want to keep our variables private, but allow read and write access to them, so each variable will be assigned a public 'get function' (or getter) and a public 'set function' (or setter) to make access easier:
class TV
{
private int channel;
private int volume;
private Display picture;
public int getChannel()
{
return( channel );
}
//was "changeChannel", but changed the name
//for consistency
public void setChannel( int newChannel )
{
channel = newChannel;
}
public int getVolume()
{
return( volume );
}
public void setVolume( int newVolume )
{
volume = newVolume;
}
public Display getPicture()
{
return( picture );
}
public void setPicture( Display newPicture )
{
picture = newPicture;
}
}
Now, what about the whole idea that my TV only has channels 1-99?
Simple, we'll change the setChannel function to only allow what we want:
class TV
{
// private variables
private int channel;
private int volume;
private Display picture;
// static constants
private static int MAX_CHANNEL = 99;
private static int MIN_CHANNEL = 1;
public int getChannel()
{
return( channel );
}
// only set the data if its in our limits.
// if it isn't, throw an exception
public void setChannel( int newChannel )
throws OutOfBoundsException
{
if( ( newChannel >= MIN_CHANNEL ) AND
( newChannel <= MAX_CHANNEL ) )
{
channel = newChannel;
}
else
{
throw new OutOfBoundsException();
}
}
public int getVolume()
{
return( volume );
}
public void setVolume( int newVolume )
{
volume = newVolume;
}
public Display getPicture()
{
return( picture );
}
public void setPicture( Display newPicture )
{
picture = newPicture;
}
}
If you don't understand the exception, don't worry. The point is that we ONLY assign the value to the variable if the info sent is within our rules. If the exception is still throwing you off, another way to do it would be to write it like so:
...
public boolean setChannel( int newChannel )
{
if( ( newChannel >= MIN_CHANNEL ) AND
( newChannel <= MAX_CHANNEL ) )
{
channel = newChannel;
return( true ); //it WAS set
}
else
{
return( false ); //it was NOT set
}
}
...
Now, we have complete control over how our variables are set.<BR><BR>
Feel free to ask questions.
class TV
{
int channel;
int volume;
Display picture;
void changeChannel( int newChannel )
{
channel = newChannel;
}
}
Now, when we make a class, we have a purpose for it. This is supposed to be stuff for a TV, but we are allowing the user access to all the functions AND all the variables. That is a problem. If my TV only has channels 1-99, what happens if I write this line:
tvObject.channel = 284;
It is a runtime error. I'm directly accessing the variable and setting it myself. Well, when classes were developed, this problem was addressed. The class designer is given the ability to assign access to each item within a class, so that the class user will be forced to use the class in the way it was intended. This is known as encapsulation. There are three levels of access:
private: Any item (variable or function) assigned a private access can ONLY be used from within the class itself. If we call the channel variable private like so:
...
private int channel;
...
Then the user cannot set the channel outside the class:
void main()
{
TV tvObject = new TV();
tvObject.channel = 283;
}
But, since the changeChannel() function is within the class, it CAN use the channel variable.
public: this access allows anyone, anywhere to use the item in the class.
protected: this access allows only the class, itself to access the item (like private), but it also allows children of the class to use the item (don't worry about this one until next lesson).
As a rule of thumb, all variables should have a private access, and all functions should have a public access.
Lets rewrite our class:
class TV
{
private int channel;
private int volume;
private Display picture;
public void changeChannel( int newChannel )
{
channel = newChannel;
}
}
Now, we have limited access too much in our class. We want to keep our variables private, but allow read and write access to them, so each variable will be assigned a public 'get function' (or getter) and a public 'set function' (or setter) to make access easier:
class TV
{
private int channel;
private int volume;
private Display picture;
public int getChannel()
{
return( channel );
}
//was "changeChannel", but changed the name
//for consistency
public void setChannel( int newChannel )
{
channel = newChannel;
}
public int getVolume()
{
return( volume );
}
public void setVolume( int newVolume )
{
volume = newVolume;
}
public Display getPicture()
{
return( picture );
}
public void setPicture( Display newPicture )
{
picture = newPicture;
}
}
Now, what about the whole idea that my TV only has channels 1-99?
Simple, we'll change the setChannel function to only allow what we want:
class TV
{
// private variables
private int channel;
private int volume;
private Display picture;
// static constants
private static int MAX_CHANNEL = 99;
private static int MIN_CHANNEL = 1;
public int getChannel()
{
return( channel );
}
// only set the data if its in our limits.
// if it isn't, throw an exception
public void setChannel( int newChannel )
throws OutOfBoundsException
{
if( ( newChannel >= MIN_CHANNEL ) AND
( newChannel <= MAX_CHANNEL ) )
{
channel = newChannel;
}
else
{
throw new OutOfBoundsException();
}
}
public int getVolume()
{
return( volume );
}
public void setVolume( int newVolume )
{
volume = newVolume;
}
public Display getPicture()
{
return( picture );
}
public void setPicture( Display newPicture )
{
picture = newPicture;
}
}
If you don't understand the exception, don't worry. The point is that we ONLY assign the value to the variable if the info sent is within our rules. If the exception is still throwing you off, another way to do it would be to write it like so:
...
public boolean setChannel( int newChannel )
{
if( ( newChannel >= MIN_CHANNEL ) AND
( newChannel <= MAX_CHANNEL ) )
{
channel = newChannel;
return( true );
}
else
{
return( false );
}
}
...
Now, we have complete control over how our variables are set.<BR><BR>
Feel free to ask questions.
Constructor short-cut (Score:2)
class hotdog {
private boolean wantMustard;
hotdog(
this.wantMustard = wantMustard;
}
}
Re:Constructor short-cut (Score:2)
You wrote:
class hotdog {
private boolean wantMustard;
hotdog(
this.wantMustard = wantMustard;
}
}
But, if your setWantMustard has rules attached, you want it to look like:
...
hotdog(
setWantMustard( wantMustard);
}
...
Not to set the variable directly.
another thing my kids asked about... (Score:2)
Since (but more towards inheritence) a subclass can view even the protected info of the parent class, they were setting variables inherited from the parent like they were local variables.
My comment was- you have 1 way of doing it (call the set method) and then later on you have only 1 place to make a code update- not 50.
A good project would be to have a class support legacy code that is littered with poor coding practices. That'd learn them REAL GOOD.
Re:another thing my kids asked about... (Score:1)
From what I've read, that's what many professional software projects are all about. Check out the book Refactoring [refactoring.com].
Re:another thing my kids asked about... (Score:2)
Whats great is that my office mate has been around since the beginning, so he can put a story to why these two similar
I'll have to read that book (if its good, I'll get work to buy a copy)
Re:Constructor short-cut (Score:2)
Now you come in here all stinkin' of Gin, honking wildy, and talking about how you don't want to set variables directly and stuff, introducting all sorts of rules and logic and stuff that we were so happy to ignore, but now we can't, can we?
I mean, if I went around saying that I was a Programmer because some moistened bint didn't let me set variables directly, people would put me away!
;)
Re:Constructor short-cut (Score:2)
Actually, one of my mentors had me avoid this like the plague. Using this with methods messes up stuff when you overload it in inheritance and when working with polymorphism and stuff. Since you can't overload variables in Java (yet), it won't be an issue with them, but you still won't see me do it (but I won't complain if I see it).
FYI - I used italics for this to refer to the keyword this (used to refer to the object currently allocated from with the class structure).
Re:Constructor short-cut and setting vars (Score:1)
however, i'm open to suggestion + instruction. i'm thinking of the caution about recursive methods: make them private, so that the client can't give them starting conditions that will never halt, i.e. warmSilicon = factorial(-1). give the client access to a method that checks for future halting conditions before starting recursion. when i first heard it, it sounded just as daft as not letting a constructor access a class variable.
but is this a similar scenario?
Re:Constructor short-cut and setting vars (Score:2)
Re:Constructor short-cut and setting vars (Score:1)
and a look at the internals of U Waterloo's 'Maple' symbolic mathematics program (part of a second year calculus course) convinced me of this.
i sit corrected. and this discussion gets added to my fortune cookie program.