Encapsulation and the meaning of control.

Encapsulation is the concept that we as developers should control how a user of our code will access the underlying data. This idea provides the benefit of allowing the developer to decide exactly how the class is used and how the underlying data is manipulated. This also prevents the users of our classes from using them incorrectly and abstracts the users from having to know exactly how our class works.

One of the easiest ways of accomplishing Encapsulation is by setting your variable to privates and then control the access through the use of getters and setters. It has become common practice that when you create a new global variable in a class, you make it private, and write a simple getter and setter for that variable. It is so common now that my IDE does it automatically for me with some comments about how it was auto-generated. Encapsulation needs to be implemented in your code, however it's commonly dumbed down the purpose of a class to simply hold values assigned to it, and return them blindly. This in effect removes the benefits of encapsulation while still applying to the rule.

public class Circle {

 private double radius;
 private double diameter;
 private double area;
 private double circumference;

 public double getRadius() {
 return radius;
 }
 public void setRadius(double radius) {
 this.radius = radius;
 }
 public double getDiameter() {
 return diameter;
 }
 public void setDiameter(double diameter) {
 this.diameter = diameter;
 }
 public double getArea() {
 return area;
 }
 public void setArea(double area) {
 this.area = area;
 }
 public double getCircumference() {
 return circumference;
 }
 public void setCircumference(double circumference) {
 this.circumference = circumference;
 }
}

This class is perfectly encapsulated, so this will pass all you quality checks and gates that would prevent a variable from be directly accessed by an outside caller. However, nothing is preventing any outside caller from directly manipulating the data though, defeating the original benefit of encapsulation. This version of Circle could be created and manipulated like this:

public void createCircle(){

 Circle c = new Circle();
 c.setRadius(3);
 c.setDiameter(6);
 // area = Π * radius^2
 c.setArea(Math.PI * (3^2));
 // circumference = 2Π * radius^2
 c.setRadius(2 * Math.PI * 3);

}

Often times you'll see a developer creating and populating the variables of a class step by step, using their own custom logic. This allows the caller to set the variables directly and doesn't prevent error in creating the circle. Say you've created the circle but have made a logic error in your math, this means that when you call the variables using the get methods, you could get incorrect data, and the caller would have no idea why. In this instance, the user populating the variables has made an error and is writing the calculated circumference to the radius variable, which will get incorrect values when called, e.g. 0 in circumference and an incorrect value in radius now. How do we fix this and prevent the caller from making math errors and incorrectly populate the Circle class?

Use constructors.

A good rule of thumb when creating objects is to think that when you create that object in your code does it exist in real life? When the circle object is created above, it doesn't actually exist. It has no radius, no diameter, no area or circumference, nothing that says that it is a circle. So why would you create it there? There's no reason to have a mythical circle. If you really had to create the reference there, you should set it to null as it represents the non-existence of the object. Then use a properly created constructor to populate the class variables as needed.

Eliminate unnecessary variables

You should only store variables that cannot be computed from another variable. In our case, if the Circle in constructed with the radius, then we do no need to store any of the other parameters because they can be computed when needed. This also will eliminate the unnecessary creation of long standing references that are held unnecessarily in the Circle object.

Another example would be for a Person. A Person would have a first and last name. So they should have independent variables for both the first and the last name. However, you wouldn't need to store the full name in a variable because it could be created using the first and last name appended to each other with a space in between.

Have your getters and setters do real work

Instead of having the getters and setters providing blanket access directly to the variables, have them do the computation unique to the idea of the getter and setter method. In the Circle case, the getDiameter() method should be changed to compute the diameter when the getDiameter() method is called. The setDiameter() method could be changed to take the diameter, compute the radius from it to store locally, or the setDiameter() could be removed all together. Let's apply these ideas to our circle class from before:

public class Circle { 
 private double radius;

 public Circle(double radius){
 if(isParameterValid(radius)){ throw new InvalidParameterException();}

 this.radius = radius;
 }

 public double getRadius() {
 return radius;
 }

 public void setRadius(double radius) {
 if(isParameterValid(radius)){ throw new InvalidParameterException();}

 this.radius = radius;
 }

 public double getDiameter() {
 return radius * 2;
 }

 public void setDiameter(double diameter) {
 if(isParameterValid(diameter)){ throw new InvalidParameterException();}

 this.radius = (diameter / 2);
 }

 public double getArea() {
 return Math.PI * (Math.pow(radius, 2));
 }

 public void setArea(double area) {
 if(isParameterValid(area)){ throw new InvalidParameterException();}

 this.radius = Math.sqrt(area / Math.PI);
 }

 public double getCircumference() {
 return 2 * Math.PI * radius;
 }

 public void setCircumference(double circumference) {
 if(isParameterValid(circumference)){ throw new InvalidParameterException();}

 this.radius = circumference / (2 * Math.PI);
 }

 private boolean isParameterValid(double parameter){
 return (parameter > 0);
 }
}

A constructor in our class eliminates the default constructor and thereby removes the ability for a user to create a version of our class that doesn't actually represent it. A circle cannot exists without a positive non-zero radius, and now we make sure that the circle can only be created in those parameters.

We have eliminated all the unnecessary variables associated with the original circle. All the values of a circle can be derived from the radius, and the radius can be derived from all the values associated with a circle, so now we are only storing the radius as a variable. This helps prevent the overhead from keeping and unnecessary number of references in memory, but also this prevents any value associated with this circle to become un-synced from each other. Anytime any value is updated through a setter, all the other values associated with this circle will then be updated as well.

Lastly, our getters and setters are now doing real work. In our second Circle class, our code is fully encapsulated, but also we only need to have getters and setters for the radius variable. The rest of the getters and setters are there to provide the consumer the objects that they assume would be available by a Circle, but do not necessarily encapsulate an underlying variable. Now when someone wants to set a value into our circle, instead of blindly setting the underlying variable, we will make sure the parameter passed is valid for our object. In this case a positive non-zero integer is required, and then method will convert the parameter into the radius and store it.

Remember, proper encapsulation means to control the access to and from the underlying variables in a class. This will be different between different entities but should not just provide the user of the class blanket access into the underlying variables. As the developer of the class, make sure the class has complete control over the underlying variables so that they are only used the way the class wants them to be used.