DEV Community

Cover image for Abstraction and Encapsulation - [OOP & Java #1]
Liu Yongliang
Liu Yongliang

Posted on • Updated on

Abstraction and Encapsulation - [OOP & Java #1]

Note: This series is for my revision of content and learning consolidation. Posting it here because I like DEV's code highlighting. Join me if you want to refresh your memory on OOP & Java.
INTRO (Some Java basics)
  • Different from Python, Java needs to be compiled before running
  • Acquiring type awareness is a must when writing Java programs
  • Java focuses on object-oriented modeling where everything is within some classes
  • Java Memory Model: Stack for function executions, Heap for object storage and garbage collection, Non-heap(Metaspace) for static fields etc.
OOP

I think there are a few key points about OOP that is different from a standard process-oriented way of writing code:

  • Model your code based on real-life objects
  • Everything else is an interaction between objects

Supposed we want to find out if a point is within a circle, what do we do? Basic programming knowledge tells us that we need to write an algorithm, or more precisely a function that describes the procedures that a computer will follow. Let's say we go with the following:

boolean contains(double pointX, double pointY, double centreX, double centreY, double radius){
    // by distance between two points on a graph formulae
    double dx = pointX - centreX;
    double dy = pointY - centreY;
    double distance = math.sqrt(dx*dx + dy*dy); 
    return distance < radius;
}
Enter fullscreen mode Exit fullscreen mode

It will work, but can we do better?

The first observation is that there are five parameters. Do you want to use a function, possibly written by others, that requires you to input 5 different parameters in a specific order? It is definitely error-prone and the user has to figure out which double value goes to where. So, Data Abstraction to the rescue.

Instead of having multiple parameters, many of them are mere numbers, we can start thinking of the problem by modeling two objects. The aim here is to reduce the parameters down to two. Here's one way to do it:

class Point {
    double x;
    double y;
}
class Circle {
    Point centre;
    double radius;
}
boolean contains(Point p, Circle c){
    double dx = p.x- c.x;
    double dy = p.y- c.y;
    double distance = math.sqrt(dx*dx + dy*dy); 
    return distance < c.radius;
}
Enter fullscreen mode Exit fullscreen mode

Now, if we give somebody the above function, he is less likely to make mistakes. Here, I equate data abstraction to:

Reduce and make meaningless parameters meaningful

Instead of passing around simple pieces of data, now intelligent things that know thyself are flowing through our program. Also, notice that our program in fact grew in size. Abstraction is more of a better organization of code than simple deletion.


The next logical leap is that the function "contains" is too wordy. How might we be able to break it down further? Notice that the word "contains" tells one whether something is within another thing. What we are doing in the function is more than that. We calculate the distance between two points, then we ask if the distance is less than the radius. The calculation of distance is not "contains". We might be able to take it out like this:

boolean contains(Point p, Circle c){
    double d = distance(p,c.centre);
    return d<c.radius;
}
double distance(Point a, Point b){
    double dx = a.x- b.x;
    double dy = a.y- b.y;
    return math.sqrt(dx*dx + dy*dy); 
}
Enter fullscreen mode Exit fullscreen mode

Now both functions do exactly like what they are named after:

  • distance() returns the distance
  • contains() returns true or false

This is functional abstraction, to which I say:

Reduce and make meaningless lines of code meaningful


The idea of encapsulation comes about because our code is like a naked man. We might want to put on some clothes for two reasons, preventing people from seeing and touching.

In the above example, we use two numbers to represent coordinates. But, someone could change the representation to using an array. This is the knowledge that is too low level and dangerous because it is subjected to changes.

Much like we have pants for our lower body and shirts for our upper body, we package data into classes in Java. One crucial question is where do we package the functions that work on these data. If we remember that we model things like real-life objects, then we can answer the question by saying that the function can be stored with the one that does the action or the one that receives the action, or both.

The reason why we avoid having it in both places is obvious:

  • unnecessary code
  • prevent inter-dependence

Hence, most of the time we should put relevant functions in reasonable classes. If you coded a function call "contains", it might make more sense to put it within the Circle class since a circle contains a point.

class Point{
    double x;
    double y;
    double distance(Point b){
        double dx = this.x- b.x;
        double dy = this.y- b.y;
        return math.sqrt(dx*dx + dy*dy); 
    }
}
class Circle {
    Point centre;
    double radius;
    boolean contains(Point p, Circle c){
        double d = p.distance(c.centre);
        return d<c.radius;
    }
}
Enter fullscreen mode Exit fullscreen mode

The second part of the story is preventing contact. In Java, we have access modifiers such as public and private to disallow the modification of values of instance variables in one class from another class.

class Impt {
    private int x;
    void changeSelf(){ // acceptable
        x=1;
    }
    void changeOther(Impt p){ // acceptable
        p.x = 1;
    }
}

class Client {
    void change(Impt p){ // illegal access
        p.x =1;
    }
}      
Enter fullscreen mode Exit fullscreen mode

One note on the private access:

It means class-level access, not object-level access. Hence, a function in a class can access the internals of an instance of the same class, even when marked as private. You can also think of it as objects of the same type are both implementer. This is not a client implementer relationship. Hence, there is no abstraction barrier within the two implementer. See the stack overflow post for more:

class Person
{
   private BankAccount account;

   Person(BankAccount account)
   {
      this.account = account;
   }

   public Person someMethod(Person person)
   {
     //Why accessing private field is possible?

     BankAccount a = person.account;
   }
}

Please forget about the design. I know that OOP specifies that private objects are private to the class. My question…



The last two concepts are somewhat new to me and hence I would like to pen down my thoughts on each of them.

Tell-Don't-Ask

Tell an object what to do, rather than asking an object for data and acting on it.


boolean contains(Point p, Circle c){
// tell a point to give me distance
    double d = p.distance(c.centre);
    return d<c.radius;
}
// vs
boolean contains(Point p, Circle c){
// ask a point to give me its x and y values so that I can 
// calculate the distance
    double dx = p.x- c.x;
    double dy = p.y- c.y;
    double distance = math.sqrt(dx*dx + dy*dy); 
    return distance < c.radius;
}
Enter fullscreen mode Exit fullscreen mode

It is important to understand this concept in the context of object interaction. Following the above example, the method "contains" in the Circle class will not ask for coordinates from the Point and calculate it. It will much prefer the distance to be calculated by the Point class and returned for comparison.

Immutability

void methods that mutate states should be avoided

Point setX(double x){
// does not mutate the original point
    return new Point(x, this,y);
}
// vs
void setX(double x){
// mutates the original point
    this.x = x;
}
Enter fullscreen mode Exit fullscreen mode

This is for the sake of consistency in testing.


To me, the two principles basically discourage the use of getters and setters. I think there is probably some situation where setters and getters are useful.

Summary

Abstraction

  • Data abstraction
  • Function abstraction

Encapsulation

  • Packaging
  • Information hiding

Principle of Good OOP Design

  • Tell-Don't-Ask
  • Immutability

Top comments (5)

Collapse
 
rukundob451 profile image
Benjamin Rukundo

I like the way you have been able to talk about each point in detail and bring out the gist of it.
Keep going indeed Liu.

Collapse
 
jouo profile image
Jashua • Edited

Nice series, I'm going to read all of them :)

OOP:
Model your code based on real-life objects

Personally, I'm not a fan of that concept, take a look at this part of the video from Robert C. Martin, just 35 seconds (51:10 - 51:45)

Collapse
 
tlylt profile image
Liu Yongliang

Hey,

I watched the talk and found some interesting ideas that you are pointing at, and I agree with them.

Is-a relationship is somewhat a lie

  • A Square class represents a square.
  • A Rectangle class represents a rectangle.
  • In real life, a square is a rectangle. But in code,
  • "The representatives of things do not share the relationships of the things that they represent."

And

OO is about managing dependencies by selectively inverting certain key dependencies to prevent rigidity, fragility and non re-usability.

In a sense, I feel that OO does model the code based on real-life objects, just that those objects are now more independent that they really are. It is definitely not a 100% copy-paste from the real world.

Also, I like the part on the open-close principle.
Like what was mentioned in the video, we are hardly able to predict what clients/consumers may want now and in the future. Even if we can safeguard ourselves from possible changes that we are able to predict at the point of coding, (e.g. that clients may want to add an Oval in our classes of shapes), there are other aspects which will eventually force us to change our design. In this case, open-close principle is simply our desire and our hopes and dreams. He mentioned that we can try to release early and get a sense of consumer thinking behind a product and therefore implement accordingly. That might work to an certain extent.
Like most things in life, human beings are the source of failure. 😂

Thanks for sharing!

Collapse
 
jouo profile image
Jashua

Yea! I completely agree :)

Your series are really good, hands down, are design patterns in your list of things to write about?

Thread Thread
 
tlylt profile image
Liu Yongliang

Thank you for your kind words 😄

I did plan to write about design patterns and but maybe not anytime soon due to my other commitments. However, my primary source of information on design patterns is this website(Do check it out if you haven't).

Cheers!