Remember that encapsulation thing?

So far we have seen what really only amounts to a kind of code-organizing convention; although we did discuss the wider goals of OOP. So now we will take things further and begin to see how we manage to achieve encapsulation with OOP.

Tip

Definition of encapsulation

Keeping the internal workings of your code safe from interference from the programs that use it while allowing only the variables and methods you choose, to be accessed. This means your code can always be updated, extended or improved without affecting the programs that uses it if the exposed parts are still made available in the same way. It also allows the code that uses your encapsulated code to be much simpler and easier to maintain because much of the complexity of the task is encapsulated in your code.

But didn't you say we don't have to know what is going on inside? So you might question what we have seen so far like this.

Note

Reasonable OOP student question: If we are constantly setting the instance variables like this rambo.health = 100;, isn't it possible that eventually, things could start to go wrong, perhaps like this:

rambo.soldierType = "fluffy bunny";

This is a very real danger that encapsulation can prevent.

Encapsulation protects your class/objects of your class/code from being used in a way that it wasn't meant to be. By strictly controlling the way that your code is used it can only ever do what you want it to do and with value ranges that you can control.

It can't be forced into errors or crashes. Also, you are then free to make changes to the way your code works internally, without breaking any other games or the rest of your game, that might be written using an older version of the code.

weightlifter.legstrength = 100;
weightlifter.armstrength = -100;
weightlifter.liftHeavyWeight();

// one typo and weightlifter will rip off his arms

We can encapsulate our classes to avoid this and here is how.

Controlling class use with access modifiers

The designer of the class controls what can be seen and manipulated by any program that uses their class. Let's look at the access modifier you have probably already noticed before the class keyword like this:

public class Soldier{
   //Implementation goes here
}

There are two main access modifiers for classes in the context we have discussed so far. Let's briefly look at each in turn:

  • public. This is straightforward. A class declared as public can be "seen" by all other classes.
  • default. A class has default access when no access modifier is specified. This will make it public but only to classes in the same package and inaccessible to all others.

So now we can make a start at this encapsulation thing. But even at a glance, the access modifiers described are not very fine grained. We seem to be limited to:

  • Completely locking down to anything outside of the package (default)
  • or a complete free-for-all (public)

The benefits here are easily taken advantage of, however. The idea would be to design a package that fulfills a set of tasks. Then all the complex inner workings of the package, the stuff that shouldn't be messed with by anybody but our package, should be default access (only accessible to classes within the package). We can then make available a careful selection of public classes that can be used by others (or other distinct parts of our program).

Class access in a nutshell

A well-designed game will probably consist of one or more packages, each containing only default or default and public classes.

In addition to class level privacy controls, Java gives us programmers more fine-grained controls, but to use these controls we have to look into variables with a little more detail.

Controlling variable use with access modifiers

To build on the class visibility controls, we have variable access modifiers. Here is a variable with the private access modifier being declared.

private int myInt;

For example, here is an instance of our Soldier class being declared, created and assigned. As you can see the access specified in this case is public.

public Soldier mySoldier = new Soldier();

Tip

Before you apply a modifier to a variable you must first consider the class visibility. If class a is not visible to class b, say because class a has default access and class b is in another package, then it doesn't make any difference what access modifiers you use on the variables in class a, class b can't see any of them anyway.

So, what can we surmise about encapsulation design so far? It makes sense to show a class to another class when necessary but only to expose the specific variables that are needed; not all of them.

Here is an explanation of the different variable access modifiers. They are more numerous and finely grained than the class access modifiers.

  • public: You guessed it, any class or method from any package can see this variable. Use public only when you are sure this is what you want.
  • protected: This is the next least restrictive after public. Protected variables can be seen by any class and any method if they are in the same package.
  • default: Default doesn't sound as restrictive as protected but it is more so. A variable has default access when no access is specified. The fact that default is restrictive perhaps implies we should be thinking on the side of hiding our variables more than we should be exposing them.

    Note

    At this point, we need to introduce a new concept. Do you remember we briefly discussed inheritance and how we can quickly take on the attributes of a class by using the extends keyword? Just for the record, default access variables are not visible to subclasses; that is, when we extend a class like we did with Activity, we cannot see it's default variables. We will look at inheritance in more detail later in the chapter.

  • private: Private variables can only be seen within the class they are declared. Including, like default access, they cannot be seen by subclasses (classes that inherit from the class in question).

Tip

The depth and complexity of access modification are not so much in the range of modifiers but rather in the smart ways we can combine them to achieve the worthy goals of encapsulation.

Variable access summary

A well-designed game will probably consist of one or more packages, each containing only default or default and public classes. Within these classes, variables will have carefully chosen and most likely varied access modifiers, chosen with a view to achieving our goal of encapsulation.

One more twist in all this access modification stuff before we get practical with it.

Methods have access modifiers too

It makes sense because methods are the things that our classes can do. We will want to control what users of our class can and can't do.

The general idea here is that some methods will do things internally only and are, therefore, not needed by users of the class and some methods will be fundamental to how users of the class, well, use your class.

The access modifiers for methods are the same as for the class variables. This makes things easier to remember but suggests, again, that successful encapsulation is a matter of design rather than of following any specific set of rules.

As an example, this next method, provided it is in a public class, could be used by any other class that has instantiated an object of the appropriate type.

public useMeEverybody(){
   // do something everyone needs to do here
}

Whereas this method could only be used internally by the class that contains it.

private secretInternalTask(){
   /*
      do something that helps the class function internally
      Perhaps, if it is part of the same class,
      useMeEverybody could use this method-
      On behalf of the classes outside of this class.
      Neat!
   */
}

And this next method with no access specified has default visibility. It can be used only by other classes in the same package. If we extend the class containing this default access method it will not have access to this method.

fairlySecretTask(){
   // allow just the classes in the package
   // Not for external use
}

As the last example before we move on, here is a protected method, only visible to the package, but usable by our classes that extend it, just like onCreate.

protected packageTask(){
   // Allow just the classes in the package
   // And you can use me, if you extend me, too
}

Method access summary

Method access should be chosen to best enforce the principles we have already discussed. It should provide the users of your class with just the access they need and preferably nothing more. Thereby we achieve our encapsulation goals, like keeping the internal workings of your code safe from interference from the programs that use it, for all the reasons we have discussed.

Accessing private variables with getters and setters

Now we need to consider, if it is best practice to hide our variables away as private, how do we allow access to them, where necessary, without spoiling our encapsulation? What if an object of class Hospital wanted access to the health member variable from an object of type Soldier, so it could increase it? The health variable should be private because we don't want just any piece of code changing it.

To be able to make as many member variables as possible private and yet still allow limited access to some of them, we use getters and setters. Getters and setters are just methods that get and set variable values.

Getters and setters are not some special new Java thing we have to learn. It is just a convention for using what we already know. Let's have a look at getters and setters using our Soldier and Hospital class example.

In this example, each of our two classes is created in their own file but the same package. First, here is our Hospital class.

class Hospital{
   private void healSoldier(Soldier soldierToHeal){
      int health = soldierToHeal.getHealth();
      health = health + 10;
      soldierToHeal.setHealth(health);
   }
}

Our implementation of the Hospital class has just one method, healSoldier. It receives a reference to a Soldier object as a parameter. So, this method will work on whichever Soldier object is passed in, vassily, wellington, rambo or whoever.

It also has a local health variable which it uses to temporarily hold and increase the soldier's health. In the same line, it initializes the health variable to the Soldier object's current health. The Soldier object's health is private so the public getter method getHealth, is used instead.

Then health is increased by 10 and the setHealth setter method loads up the new revived health value, back to the Soldier object. Perhaps the value 10 could be changed to a member variable of Hospital. Then, in the RTS, as the hospital is upgraded, the amount the health is restored by could increase.

The key here is that although a Hospital object can change a Soldier object's health; it only does so within the bounds of the getter and setter methods. The getter and setter methods can be written to control and check for potentially erroneous or harmful values.

Next look at our hypothetical Soldier class with the simplest implementation possible of its getter and setter methods.

public class Soldier{
   
   private int health;

   public int getHealth(){
          return health;
   }

   public void setHealth(int newHealth){

      // Check for stupid values of newHealth
      health = newHealth;
   }
}

We have one instance variable called health and it is private. Private means it can only be accessed by methods of the Soldier class. We then have a public getHealth method which unsurprisingly returns the value held in the private health int variable. As this method is public, any code with access to an object of type Soldier can use it.

Next, the setHealth method is implemented. Again, it is public but this time it takes an int as a parameter and assigns whatever is passed in, to the private health variable.

Tip

In a more life-like example, we would write some more code here to make sure the value passed in to setHealth is within the bounds we expect.

Now we declare, create, and assign to make an object of each of our two new classes and see how our getters and setters work.

Soldier mySoldier = new Soldier();
// mySoldier.health = 100;// Does not work, private

// we can use the public setter setHealth()
mySoldier.setHealth(100);// That's better

Hospital militaryHospital = new Hospital();

// Oh no mySoldier has been wounded
mySoldier.setHealth(10);

/*      
   Take him to the hospital.
   But my health variable is private
   And Hospital won't be able to access it
   I'm doomed - tell Laura I love her

   No wait- what about my public getters and setters?
   We can use the public getters and setters 
   from another class
*/

militaryHospital.healSoldier(mySoldier);

// mySoldier's private variable, health has been increased by 10
// I'm feeling much better thanks!

We see that we can call our public setHealth and getHealth methods directly on our object of type Soldier. Not only that, we can call the healSoldier method of the Hospital object, passing in a reference to the Soldier object, which too can use the public getters and setters to manipulate the private health variable.

We see that the private health variable is simply accessible, yet totally within the control of the designer of the Soldier class.

Note

Getters and setters are sometimes referred to by their more correct names, Accessors, and Mutators. We will stick to getters and setters. I just thought you might like to know the jargon.

Yet again our example and the explanation is probably raising more questions than it answers. That's good.

By using encapsulation features (like access control) it is kind of like agreeing on an important deal/contract about how to use and access a class, its methods, and variables. The contract is not just an agreement about now, but an implied guarantee for the future. We will see that as we proceed through this chapter, there are more ways that we refine and strengthen this contract.

Tip

Use encapsulation where it is needed or, of course, if you are being paid to use it by an employer. Often encapsulation is overkill on small learning projects, like some of the examples in this book. Except, of course, when the topic you are learning is encapsulation itself.

We are learning this Java OOP stuff under the assumption that you will one day want to write much more complex games, whether on Android or some other platform which uses OOP. In addition, we will be using classes from the Android API that use it extensively and it will help us understand what is happening then as well. Typically, throughout this book, we will use encapsulation when implementing full game projects and often overlook it when showing small code samples to demonstrate a single idea or topic.

Setting up our objects with constructors

With all these private variables and their getters and setters, does it mean that we need a getter and a setter for every private variable? What about a class with lots of variables that need initializing at the start. Think about this code:

mySoldier.name
mysoldier.type
mySoldier.weapon
mySoldier.regiment
...

Some of these variables might need getters and setters, but what if we just want to set things up when the object is first created, to make the object function correctly?

Surely, we don't need two methods (a getter and a setter) for each?

Fortunately, this is unnecessary. For solving this potential problem there is a special method called a constructor. We briefly mentioned the existence of a constructor when we discussed instantiating an object from a class. Let's take a look again. Here we create an object of type Soldier and assign it to an object called mySoldier.

Soldier mySoldier = new Soldier();

Nothing new here but look at the last part of that line of code.

...Soldier();

This looks suspiciously like a method call.

All along, we have been calling a special method called a constructor that has been created, behind the scenes, automatically, by the compiler.

However, and this is getting to the point now, like a method, we can override it which means we can do useful things to set up our new object before it is used. This next code shows how we could do this.

public Soldier(){
   // Someone is creating a new Soldier object
   
   health = 200;
   // more setup here
}

The constructor has a lot of syntactical similarities to a method. It can however only be called with the use of the new keyword and it is created for us automatically by the compiler- unless we create our own like in the previous code.

Constructors have the following attributes:

  • They have no return type
  • They have the exact same name as the class
  • They can have parameters
  • They can be overloaded

One more piece of Java syntax which is useful to introduce at this point is the Java this keyword.

The this keyword is used when we want to be explicit about exactly which variables we are referring to. Look at this example constructor, again for a hypothetical variation of the Soldier class.

public class Soldier{

   String name;
   String type;
   int health;


   public Soldier(String name, String type, int health){

      // Someone is creating a new Soldier object
      
      this.name = name;
      this.type = type;
      this.health = health;

      // more setup here
   }
}

This time the constructor has a parameter for each of the variables we want to initialize.

Tip

A key point to note about constructors that can cause confusion is that once you have provided your own constructor, perhaps like the previous one, the default one (with no parameters) no longer exists.

Let's discuss this a bit more.

Using "this"

When we use this as we do in the previous lines of code we are referring to the instance of the class itself. By using the this keyword it is clear when we mean the member variable or the parameter.

As another common example, many things in Android require a reference to an instance of Activity to do its job. We will fairly regularly, throughout this book pass in this (a reference to the Activity) in order to help another class/object from the Android API do its work. We will also write classes that need this as an argument in one or more of its methods. So, we will see how to handle this when it is passed in as well.

There are more twists and turns to be learned about variables and this and they make much more sense when applied to a practical project. In the next mini-app, we will explore all we have learned so far in this chapter and some more new ideas too.

First a bit more OOP.

Static methods

We know quite allot about classes; how to turn them into objects and use their methods and variables. But something isn't quite right? Since the very start of the book, we have been using a class that doesn't conform to everything we have learned in this chapter- so far. We have used Log to output to the logcat window, but have not instantiated it once! How can this be?

The static methods of classes can be used, without first instantiating an object of the class.

Tip

We can think of this as a static method belonging to the class and all other methods belonging to an object or an instantiation of a class.

And as you have probably realized by now, Log contains static methods. To be clear: Log contains static methods but Log is still a class.

Classes can have both static and regular methods as well, but the regular methods would need to be used in a regular way, via an instance of the class.

Take another look at Log.d in action.

Log.d("Debugging","In newGame");

Here, d is the method being statically accessed and the method takes two parameters, both of type String.

Note

More uses for the static keyword

The static keyword also has another consequence, for a variable especially when it is not a constant (can be changed) and we will see this in action in our next mini-app.

Static methods are often provided in classes which have uses that are so generic it doesn't make sense to have to create an object of the class. Another useful class with static methods is Math. This class is a part of the Java API, not the Android API.

Tip

Want to write a calculator app? It's easier than you think with the static methods of the Math class. You can look at them here: http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html

If you try this out you will need to import the Math class, the same way you imported all the other classes we have used.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.212.170