Forgot your password?
typodupeerror
Programming

FortKnox's Journal: OOP 102: Encapsulation 10

Journal by FortKnox
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.
This discussion has been archived. No new comments can be posted.

OOP 102: Encapsulation

Comments Filter:
  • If you don't want to get all funky with the extra variables, you can do something like this (at least in Java):

    class hotdog {
    ...
    private boolean wantMustard;
    ...

    // hotdog constructor
    hotdog( ..., wantMustard, ... ) {
    this.wantMustard = wantMustard;
    ...
    }

    ...
    }
    • Well, hang on now. First of all, I haven't discussed constructors, but looking at your code, I have a slight design point to make:

      You wrote:
      class hotdog {
      ...
      private boolean wantMustard;
      ...

      // hotdog constructor
      hotdog( ..., wantMustard, ... ) {
      this.wantMustard = wantMustard;
      ...
      }

      ...
      }

      But, if your setWantMustard has rules attached, you want it to look like:

      ...
      // hotdog constructor
      hotdog( ..., wantMustard, ... ) {
      setWantMustard( wantMustard);
      ...
      }
      ...

      Not to set the variable directly.
      • yeah that was another thing-

        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.

        • 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.

          From what I've read, that's what many professional software projects are all about. Check out the book Refactoring [refactoring.com].
          • Yep, that's pretty much all I do (even the new project prototyping is to get the new platform to behave in a way that our old code can run on it)

            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)
      • My point was that you didn't need "variable = myVariable"

        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!

        ;)

        • My point was that you didn't need "variable = myVariable"

          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).
      • ?? i dont get it. FortKnox, wantMustard is private to the class, does it make a difference / problem if you set it directly in the constructor instead of calling its set method? seems silly to have a variable that the class itself isn't supposed to see!

        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?
        • Using the TV method, we don't want to put this in the constructor: void TV( Display newPicture, int newVolume, int newChannel ) { picture = newPicture; volume = newVolume; channel = newChannel; } because that allows the class user to put in a channel greater than 99 (or less than 0), which is taken care of in the set command. So, as a rule of thumb (of course there are exceptions), you should only write to the private variables through the setters, even in the class itself. That way, you are insuring yourself that you are setting the variable according to the rules you placed on it in the setter (and if the rules ever change, you only change them in one place).
          • 10-4. i see now. i was thinking inside the box. a boolean doesn't exactly have a - ummm - acceptable range of values. not so with ints. according to my O-O instructor, half of good code deals with the fact that a user _might_ decide to give it an array of chars, size 7 by -3...
            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.

Air is water with holes in it.

Working...