Abstraction and Encapsulation - [OOP & Java #1]
Tags: computerscience, java, oop, codenewbie
Published: 2020-08-17 13:07:00

######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:
```java
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;
}
```
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:
```java
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;
}
```
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:
```java
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); 
}
```
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.
```java
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;
	}
}
```

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.

```java
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;
    }
}      
```

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:
{% stackoverflow 17027139 %}

---

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.
```java

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;
}
```

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

```java
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;
}
```
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