Tuesday, October 8, 2013

Encapsulation

Encapsulation is the ability to hide and protect data stored in Java objects. You may ask, "Who are the bad guys who want to illegally access my data?" It's not about bad guys. When a developer creates a Java class, he or she plans for a certain use pattern of this code by other classes. For example, the variable grossIncome should not be modified directly, but via a method that performs some validation procedures to ensure that the value to be assigned meets application-specific rules.

Encapsulation mechanisms enable the programmer to group data and the subroutines that operate on them together in one place, and to hide irrelevant details from the users of an abstraction

Encapsulation and Access Control

An OOP principle, encapsulation is a mechanism that protects parts of an object that need to be secure and exposes only parts that are safe to be exposed. A television is a good example of encapsulation. Inside it are thousands of electronic components that together form the parts that can receive signals and decode them into images and sound. These components are not to be exposed to users, however, so Sony and other manufacturers wrap them in a strong metallic cover that does not break easily. For a television to be easy to use, it exposes buttons that the user can touch to turn on and off the set, adjust brightness, turn up and down the volume, and so on.
Back to encapsulation in OOP, let's take as an example a class that can encode and decode messages. The class exposes two methods called encode and decode, that users of the class can access. Internally, there are dozens of variables used to store temporary values and other methods that perform supporting tasks. The author of the class hides these variables and other methods because allowing access to them may compromise the security of the encoding/decoding algorithms. Besides, exposing too many things makes the class harder to use. As you can see later, encapsulation is a powerful feature.

Java supports encapsulation through access control. Access control is governed by access control modifiers. There are four access control modifiers in Java: public, protected, private, and the default access level. Access control modifiers can be applied to classes or class members. We'll look at them in the following subsections.

Tight Encapsulation

Encapsulation refers to the combining of fields and methods together in a class such that the methods operate on the data, as opposed to users of the class accessing the fields directly. The term tight encapsulation refers to using encapsulation every time on all the fields of a class, and only providing access to the fields via methods. With tight encapsulation, no fields of an object can be modified or accessed directly; you can only access the fields through a method call.
To implement tight encapsulation, make the fields of a class private and provide public accessor (“getter”) and mutator (“setter”) methods. Because a mutator or accessor method must be invoked to access the fields of the object, tight encapsulation has several key benefits:
  • You can monitor and validate all changes to a field.
  • Similarly, you can monitor and format all access to a field.
  • The actual data type of a field can be hidden from the user, allowing you to change the data type without affecting the code that uses the object, as long as you do not alter the signatures of the corresponding accessor and mutator method.
To demonstrate, let's first look at a class that does not implement tight encapsulation. The following class, named Student1, represents a student with fields for the year (Freshman, Sophomore, Junior, Senior) and percentage grade of a student. The fields of Student1 are public and can be accessed directly:
public class Student1 {
    public String year;
    public double grade;
}
Because the class does not implement tight encapsulation, the fields of a Student1 object can take on any values. The following code is valid, although from an application point of view the values do not make sense:
Student1 s = new Student1();
s.year = "Memphis, TN";
s.grade = -24.5;
The string “Memphis, TN” is not a valid year, and we can assume that a student's grade should never be negative. With tight encapsulation, these issues can easily be avoided because users of the class cannot access its fields directly. By forcing a method call to change a value, you can validate any changes to the fields of the object.
The following Student2 class is similar to Student1 but implements tight encapsulation. It is not possible for year to be an invalid value or grade to be negative or greater than 105.0:
1. public class Student2 {
2.     private String year;
3.     private double grade;
4.
5.     public void setYear(String year) {
6.         if(!year.equals("Freshman")  &&
7.            !year.equals("Sophomore") &&
8.            !year.equals("Junior")    &&
9.            !year.equals("Senior")) {
10.              throw new IllegalArgumentException(
11.                               year + " not a valid year");
12.        } else {
13.            this.year = year;
14.        }
15.    }
16.
17.    public String getYear() {
18.        return year;
19.    }
20.
21.    public void setGrade(double grade) {
22.        if(grade < 0.0 || grade > 105.0) {
23.            throw new IllegalArgumentException(
24.                             grade + " is out of range");
25.        } else {
26.            this.grade = grade;
27.        }
28.    }
29.
30.    public double getGrade() {
31.        return grade;
32.    }
33. }
See if you can determine the result of the following statements:
Student2 s2 = new Student2();
s2.setYear("Junior");
s2.setGrade(-24.5);
Invoking setYear with the argument “Junior” changes the year field to “Junior”. Invoking setGrade with the argument 24.5 causes an IllegalArgumentException to be thrown on line 23. Due to tight encapsulation, it is not possible for the values of Student2 to contain invalid values.
The benefits of encapsulation outweigh any overhead of the additional method calls, and any good OO design uses tight encapsulation in all classes. The next section discusses another important objectoriented design concept: loose coupling.

In the preceding section, you learned that you should hide instance variables by making them private. Why would a programmer want to hide something? In this section we discuss the benefits of information hiding.
The strategy of information hiding is not unique to computer programming—it is used in many engineering disciplines. Consider the electronic control module that is present in every modern car. It is a device that controls the timing of the spark plugs and the flow of gasoline into the motor. If you ask your mechanic what is inside the electronic control module, you will likely get a shrug.
The module is a black box, something that magically does its thing. A car mechanic would never open the control module—it contains electronic parts that can only be serviced at the factory. In general, engineers use the term "black box" to describe any device whose inner workings are hidden. Note that a black box is not totally mysterious. Its interface with the outside world is well-defined. For example, the car mechanic understands how the electronic control module must be connected with sensors and engine parts.
The process of hiding implementation details while publishing an interface is called encapsulation. In Java, the class construct provides encapsulation. The public methods of a class are the interface through which the private implementation is manipulated.
Why do car manufacturers put black boxes into cars? The black box greatly simplifies the work of the car mechanic. Before engine control modules were invented, gasoline flow was regulated by a mechanical device called a carburetor, and car mechanics had to know how to adjust the springs and latches inside. Nowadays, a mechanic no longer needs to know what is inside the module.
Similarly, a programmer using a class is not burdened by unnecessary detail, as you know from your own experience. In Chapter 2, you used classes for strings, streams, and windows without worrying how these classes are implemented.
Encapsulation also helps with diagnosing errors. A large program may consist of hundreds of classes and thousands of methods, but if there is an error with the internal data of an object, you only need to look at the methods of one class. Finally, encapsulation makes it possible to change the implementation of a class without having to tell the programmers who use the class.

INTERFACES VERSUS ABSTRACT CLASSES

The next question is when should you use interfaces and when should you use abstract classes. If two or more classes have lots of common functionality, but some methods should be implemented differently, you can create a common abstract ancestor and as many subclasses inheriting this common behavior as needed. Declare in the superclass as abstract those methods that subclasses should implement differently, and implement these methods in subclasses.
If several classes don't have common functionality but need to exhibit some common behavior, do not create a common ancestor, but have them implement an interface that declares the required behavior. This scenario was not presented in the "Interfaces" section of Lesson 6, but it's going to be a part of the hands-on exercise in the Try It section of this lesson.
Interfaces and abstract classes are similar in that they ensure that required methods will be implemented according to required method signatures. But they differ in how the program is designed. While abstract classes require you to provide a common ancestor for the classes, interfaces don't.
Interfaces could be your only option if a class already has an ancestor that cannot be changed. Java doesn't support multiple inheritance — a class can have only one ancestor. For example, to write Java applets you must inherit your class from the class Applet, or in the case of Swing applets, from JApplet. Here using your own abstract ancestor is not an option.
While using abstract classes, interfaces, and polymorphism is not a must, it certainly improves the design of Java code by making it more readable and understandable to others who may need to work on programs written by you.

No comments:

Post a Comment