0% found this document useful (0 votes)
10 views

Week 2 Abstraction and Encapsulation

1. The document discusses abstraction and encapsulation in object-oriented programming. It describes defining a Point class with x and y coordinates as properties and a constructor. 2. It also discusses defining methods like distanceTo() that act on a point object, and the importance of the toString() method for returning an understandable string representation of an object. 3. When defining multiple classes, it is better to avoid cyclic dependencies between classes. For checking if a point is contained within a circle, it is better to define a contains() method in the Circle class rather than an isContainedIn() method in the Point class.

Uploaded by

Richard Wang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Week 2 Abstraction and Encapsulation

1. The document discusses abstraction and encapsulation in object-oriented programming. It describes defining a Point class with x and y coordinates as properties and a constructor. 2. It also discusses defining methods like distanceTo() that act on a point object, and the importance of the toString() method for returning an understandable string representation of an object. 3. When defining multiple classes, it is better to avoid cyclic dependencies between classes. For checking if a point is contained within a circle, it is better to define a contains() method in the Circle class rather than an isContainedIn() method in the Point class.

Uploaded by

Richard Wang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

Week 2: Abstraction and Encapsulation

Creating a point object


A point object is a composite data type with 2 properties, namely:

x coordinate ( double data type)

y coordinate ( double data type)

A point object as a composite data type also has to be defined using a constructor.

We can define the class Point using several methods:

Using double x and double y

class Point {
private final double x;
private final double y;

Point(double x, double y) {
this.x = x;
this.y = y;
}
// Methods
}

Immutable list ( Imlst<Double> ) [ Double has to be in caps, as it represents the wrapper type, while double is a primitive.]

class Point {
ImList<Double> coord;

Point(double x, double y) {
this.coord = new ImList<Double>().add(x).add(y)

Pair ( Pair<Double, Double> pair )

Object oriented programming

Week 2: Abstraction and Encapsulation 1


Usually, under the class definition, there are a few components that are to be included in this order:

1. Properties

Also called attributes in python, properties are the constituents that makes up the class. (eg. a Point object contains 2 properties; x-coordinate of double type and y-
coordinate of double type.)

2. Constructor

The constructor is a statement block that is responsible for the creation or instantiation of an object of a certain class.

Note that this.x and this.y in this case would refer to the x and y under the PROPERTIES portion of the class definition. Upon calling the constructor method, the x
and y passed in as arguments to the constructor function will be assigned to this.x and this.y respectively. The final keyword assures these initial values will be
locked and not subjected to state changes anymore.

3. Methods

Also called functions in python, methods are object specific and only act on tge object (of a defined class) that calls the methods itself. A method basically specifies the
behaviour of an object.

Lastly, a very important method to include under every class definition is the toString() method. This method enables us to get confirmation of the object we have
created after using the constructor statement in the form of a visual output in JShell.

toString( ) method
Usually, when we use the constructor to create an object, the created object will be displayed as a specific memory address as the output, which is not of much meaning.

toString() method is a way that gives us confirmation of the object that we have created using the class constructor. This is because the toString() method will display a

string denoting an instance is created instead of of displaying the output as memory address.

General syntax:

public String toString() {


return ...
}

toString() method of a class will be called by the constructor method which will be called during every instantiation of an object of a given class using the keyword new .

toString() method is NOT returning a String , instead it will return a Point , but it just uses the string method instead of the memory address to display the Point , but
its data type is NOT a string.

If one really wants to output a point of String type, we will use something like string s = new Point(0.0, 0.0).toString() .

One cannot assign a Point object to a String object as new Point(0.0, 0.0).toString() is a string object.

Lastly, one important point to note is that if you call System.out.println() on an object of a certain class instead of a primitive, the System.out.println() method will call the
toString() method defined under that specific class.

The toString() method usually uses the “+” operator to concatenate strings. As long as one of the operand is of data type string, “+” will concatenate instead of doing
addition. “+” is also called an overloaded operator as it can have different functions (concatenation and addition) depending on the type of the operands.

“a” + “b” → “ab”

Week 2: Abstraction and Encapsulation 2


“a” + 1 → “a1”

2+1→3

Functional abstraction
As compared to distanceBetween() which is a method not packaged under any class and takes in 2 Point parameters, we will use OOP to create another method called
distanceTo() which will be packaged under the Point class and only take in 1 point parameter of the target point and returns the distance from the point that calls the

method itself to any target point.

This is essentially OOP, in which every method defined is with respect to an object (which is a point in this case). This is functional abstraction, data abstraction is in a
similar notion just that it applies to the properties of the class.

Packaging
Usually, the nouns in the problem statements (eg. Point, Circle) will need to be defined as classes.

Problem Statement: Checking if any Point provided lies in a Circle.

General idea:

1. Obtain the centre Point of the circle

2. Obtain the radius of the circle

3. If the distance from the Point to the centre of the circle is less than the radius of the circle, the point lies in the circle.

boolean contains<Point point> method has to be of boolean data type as a Circle can either contain a point object or not contain a point object.

The notion of packaging: Will it be better to define a contains<Point point> which will determine whether a given point will be contained inside the circle with respect to the
circle OR define a isContainedIn<Circle circle> which will determine whether a given circle will contain the point with respect to the point??? [Which design is better???]

contains<Point point> method under the Circle class

isContainedIn<Circle circle> method under the Point class

this in circle.centre.distanceTo(this) will refer to the Point object which is calling the method isContainedIn() .

The Circle class inherently depends on the Point class, as one of its properties, which is centre, is of the Point data type. Thus, deciding between the 2 different methods
will bring us to the concept of cyclical dependency:

Notice that in the absence of both methods, the Circle class depends on the Point class, but the Point class does not depend on the Circle class.

The contains() method under the Circle class will still make Circle dependent on Point but the isContainedIn() under the Point class will make Point dependent on
Circle . Since the isContainedIn() method will invoke cyclical dependency, it would NOT be a good design.

To determine whether a class depends on another class, these are some characteristics:

The class contains properties of another class

The class requires data of another class to be passed into the constructor method

The class contains methods that requires an object of another class to be passed in as argument

The class contains methods that calls the methods defined under another class

Compiling the class results in the automatic compilation of another class

Cyclic dependencies
In essence, we would not want a class to depend on another class and the reverse to be true at the same time.

For example, if we package the isContainedIn() method in the Point class and remove the contains() method under the Circle class, Point and Circle will be cyclically
dependent:

Point depends on Circle due to the isContainedIn() method

Circle depends on Point due to the property centre, the existence of the property centre in the constructor, as well as due to the contains() method.

To remove cyclic dependency, we should package the contains() method under the Circle class and remove the isContainedIn() method under the point class.

Types of relationships

Composite relationship (”has a” relationship; Circle has a Point )

Inheritance relationship (”is a “ relationship)

Week 2: Abstraction and Encapsulation 3


Incremental development / testing
If we are designing multiple classes, we focus on the simplest class first that has no dependencies on all other classes, test out all the constructors and methods and make
sure everything works first, and then go to the next class and do the same.

If in somewhere there is a compile error or bug, it is most likely due to the secondary class.

Java Memory Model (revisited)


The new keyword will leverage on the heap space to store the new object created in addition to stack memory in the call stack to reference the new object.

There can multiple copies of references (pointers) to the same object, but there is only one copy of the object object

In OOP, when a method is called, the method stack will stack on top of the jShell stack in the call stack. More importantly, since all methods defined under a class are with
reference to an object the class itself, there will implicitly be a “this = “ entry in the method stack upon EVERY method call. It will store a reference to the object itself that is
calling the method.

In calling contains() , distanceTo() will also get called (pushed into the call stack). distanceTo() will then return a distance value of type double to the contains() method.
The distanceTo() method stack will then be popped from the stack once it returns a value. Next, contains() will return a boolean value to JShell, and will be popped from the
stack.

Encapsulation
Involves packaging (which data goes into which class, and which methods goes into which class) and information hiding.

Information hiding

“Client - Implementer” relationship; Circle (Secondary) class is the Client as it uses or depends on Point (Primary) class, which is the implementer. Bottom line is that the
client should not have to know the low level implementation of the implementer; in other words, the low level implementation of the primary class should be hidden away
from the secondary class.

Say for example we take 2 implementations of the contains() method in the Circle class:

Implementation 1:

Implementation 2:

Notice that for implementation 2, in order for the contains() method to work, the Circle class needs to know the values of the x and y properties of the Point . This
exposes to Circle that the Point class has an x-coordinate property and a y-coordinate property. Thus in the spirit of encapsulation, we will stick to the first
implementation.

This notion of information hiding is significant in that it will not place an implicit restriction HOW the Point class is being implemented. This is because, the Point class
can be represented in many ways, using 2 singletons x and y, using ImList , or using Pair . When we expose the implementation of Point class to the Circle class, it
will implicitly restrict the implementation of the original Point class to, in this case, using the singletons double x and double y. The reason being is that if Point is
actually being implemented with ImList , then the contains() function would not work as it assumes the implementation of Point as singletons.

Another way to achieve information hiding is through the use of accessors, which are methods to get the respective property values without having to explicitly specify what
the values are themselves. Thus, these accessors can be used in the secondary classes to obtain the values of the properties primary class without having to expose the
properties themselves. Consider 2 different implementations of the Point object: One using singletons and one using ImList .

class Point {
// Properties
private final double x;
private final double y;
...
// Accessors
double getX() {
return this.x;
}

double getY() {
return this.y;
}

Week 2: Abstraction and Encapsulation 4


...
}

class Point {
// Properties
private final ImList<Double> coord;
...
// Accessors
double getX() {
return this.coord.get(0);
}

double getY() {
return this.coord.get(1);
}
...
}

No matter how the Point class is implemented, all that needs to change is the implementation of its associated accessor methods, and information hiding can still be
achieved. (As long as Circle class can still call the accessor methods of Point class, the properties of Point can be hidden away from the Circle class.)

HOWEVER, even though accessors achieve encapsulation, they violate the guiding principle “Tell-Don’t-ask”.

“Tell-Don’t-ask” principle
Consider the code below:

Notice we are actually obtaining the raw values of the x-coordinate and y-coordinate through the use of accessors and in turn using this value to compute the distance
own our own (”Asking”).

Instead, since our goal is mainly to compute the distance and not to find out what the 2 points are, we should tell the Point object to compute its own distance through
defining a method under the Point class to compute distance. (”Telling”)

If you do not provide accessors to the client ( Circle class), it will restrict what the client can know. Thus, merely computing the distance and providing the distance to
the client will not expose where the 2 points are.

Thus, the “Tell” code will be shown below:

Private access modifiers


The use of accessors then brings us to the concept of private access modifiers. Even though the use of accessors are discouraged due to the “Tell-Don’t-ask” principle, we
can still keep the use of accessors WITHIN their OWN class and prevent other classes from using it. This is done through the keyword private . This will be useful as when
we decide to change the implementation of the class itself, we will just need to change the implementation of the accessors without having to change the implementation of
every other method in the class.

Thus, all accessor methods in all classes are usually local to the class itself, as they should be made private . Other classes cannot use the accessor method that is deemed
private to a specific class.

All the properties of a class must also be made private as it prevents other classes from explicitly using its properties, thus achieving the goal of information hiding as
properties of primary classes can no longer be exposed to secondary classes.

ALWAYS have to make private :

Properties

Accessors

Mutating objects (setters)


Setters are methods that changes (sets) the values of the properties. This GOES AGAINST effect free programming as the values of the properties are changed.

Below is a setter method to scale an object of circle class (change the size):

Week 2: Abstraction and Encapsulation 5


Note that setters are almost always void return type as the goal of setters is to change the value of properties which is a side-effect (state change). As setters goes
against effect free programming due to change of state of properties, they will be unable to run due to the final keyword before the properties. Thus to test effect of
setters, we will temporarily remove final .

Since our properties are final , we cannot run the scale() method as this involves assignment or change of this.radius , which is a state change. To still run the scale()

method, we will need to change it to the code below:

This new scale() method will return a new Circle object with the same centre but with the scaled radius, so the original Circle object that calls the scale() method
created would be untouched and would not change, as the “changes” are made to the new Circle object.

Any state change of the original Circle object c have to be done through manual assignment:

We will always avoid state-mutating void methods (setters) in effect-free programming, and we will return a new objects instead to circumvent it.

Abstraction Barrier
Provides a separation between usage by clients and implementation by implementer

Between every 2 classes, there will always be an abstraction barrier, thus we will need to decide what to expose and what to hide from the other classes.

CANNOT

Have state changing effects on the original constructed object (Add final in front of properties OR just nest properties in a Imlist )

Have cyclic dependencies (Make sure that ONLY methods in secondary classes relies on [uses the methods] in primary classes and NOT the other way round.)

Expose properties of classes to the clients (Use getters for primary class and privatise all getters and properties of all classes, and then do “tell not ask”.)

Week 2: Abstraction and Encapsulation 6

You might also like