Abstraction and Encapsulation – [OOP & Java #1]

OOP & Java (15 Part Series)

1 Abstraction and Encapsulation – [OOP & Java #1]
2 Inheritance and Polymorphism – [OOP & Java #2]
11 more parts…
3 Compile-Time Type Vs Run-Time Type – [OOP & Java #3]
4 Concrete Vs Abstract Vs Interface – [OOP & Java #4]
5 Generics – [OOP & Java #5]
6 Bounded Generic Types & Wildcards – [OOP & Java #6]
7 Type Inference And Generic Methods – [OOP & Java #7]
8 Comparator & Comparable – [OOP & Java #8]
9 Throw & Throws – [OOP & Java #9]
10 Unbounded Wildcards – [OOP & Java #10]
11 Generics PECS – [OOP & Java #11]
12 Calling super() – [OOP & Java #12]
13 Exploring Java Method References – [OOP & Java #13]
14 Explaining Java’s Optional -[OOP & Java #14]
15 Crossing Abstraction Barrier Between Parent & Child Class – [OOP & Java #15]

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:

图片[1]-Abstraction and Encapsulation - [OOP & Java #1] - 拾光赋-拾光赋
Access private field of another object in same class
Jun 10 ’13
Comments: 5
Answers: 9
106

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…

Open Full 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

OOP & Java (15 Part Series)

1 Abstraction and Encapsulation – [OOP & Java #1]
2 Inheritance and Polymorphism – [OOP & Java #2]
11 more parts…
3 Compile-Time Type Vs Run-Time Type – [OOP & Java #3]
4 Concrete Vs Abstract Vs Interface – [OOP & Java #4]
5 Generics – [OOP & Java #5]
6 Bounded Generic Types & Wildcards – [OOP & Java #6]
7 Type Inference And Generic Methods – [OOP & Java #7]
8 Comparator & Comparable – [OOP & Java #8]
9 Throw & Throws – [OOP & Java #9]
10 Unbounded Wildcards – [OOP & Java #10]
11 Generics PECS – [OOP & Java #11]
12 Calling super() – [OOP & Java #12]
13 Exploring Java Method References – [OOP & Java #13]
14 Explaining Java’s Optional -[OOP & Java #14]
15 Crossing Abstraction Barrier Between Parent & Child Class – [OOP & Java #15]

原文链接:Abstraction and Encapsulation – [OOP & Java #1]

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容