Abstraction Unleashed: Simple Steps to Boost Your Code Skills from Scratch to Pro

Ahmed Samir
Dev Genius
Published in
12 min readNov 24, 2023

--

Table of contents

  1. What is abstraction
  2. Abstraction in real life
  3. Abstraction in OOP
    3.1 —Introduction
    3.2 — Critical ideas to think
  4. Types of abstraction
    4.1 —Ways to achieve abstraction — Overview
    4.2 — Data abstraction
    4.3 — Control or Process abstraction
    4.4 — Example to understand control and data abstraction together
  5. Abstraction vs Encapsulation
  6. Abstract classes vs. Interfaces
  7. Points about interface
    7.1 — Constants in interface
    7.2 — The convention of giving a name to the interface
    7.3 — Default Methods in Interface
    7.4 — Private methods in interface
    7.5 — Realization Concept
  8. Points about abstract class
    8.1 — Why do we use constructor inside abstract class?
    8.2 — When to use abstract methods & abstract class
    8.3 — Rules for Abstract Class
    8.4- Abstract it is not an access modifier
  9. References

1 — What is abstraction?

Abstraction means keeping the inside workings of a program hidden from the outside (The process of hiding the internal details). It’s a way of explaining things in a simple manner and forming a barrier between the program and the programs that use it. It helps to hide implementation details.

2 —Abstraction in real life

We use a microwave to warm our food. By pressing buttons to set the timer and specify the type of food, we end up with a tasty, hot meal. The inner workings of the microwave are kept secret from us, and we’re provided access to its functions in a straightforward way.

3 — Abstraction in OOP

3.1 —Introduction

In Object-Oriented Programming, objects serve as the foundation. An object has properties and methods, and we can hide them from the external environment (outer world) using access modifiers. We can selectively grant access to specific functions and properties for other programs, following this standard approach to implement abstraction in OOPS.

Figure 1: Illustrate the notion of abstraction in object-oriented programming.

In other words, abstraction in OOPS enables the hiding of the internal details of an object from the outside world, so that the focus is on what the object does, rather than how it does it.

Don’t forget to know that the abstraction contributes to both security and reusability by leveraging polymorphism, inheritance, and encapsulation as shown in the figure below.

Figure 2: Visual representation of abstraction contributing to both security and reusability.

3.2 — Critical ideas to think

  1. An object has attributes and methods. By using access modifiers, we can hide important details and provide access to necessary methods and attributes to the outside world.
  2. Abstraction help us define objects as abstract “actors” that can perform set of operations, change their state, and communicate with other objects.
  3. We can also understand abstraction through inheritance. In inheritance hierarchy, parent classes contain general implementation, while child classes contain more specific implementation.
  4. Abstraction is a key concept behind several fundamental object-oriented programming principles like Open-Closed Principle and Dependency Inversion Principle.

4 — Types of abstraction

4.1 Ways to achieve data abstraction — Overview

1- Abstract class (partial abstraction 0 to 100 %)
2- Interface class (fully abstraction 100%) (In some java versions) I’ll explain more details in the next sections, in a part called the “default method part.

Figure 3: Demonstrates methods of abstraction.

Why don’t we achieve a complete abstraction in an abstract class?
Because we can write the implementation code in functions. But in the interface, we can’t do that.

4.2 — Data abstraction

When we keep object information a secret (Hidden) from the outside, it forms data abstraction. If needed, we allow access to that info using clear public methods, commonly known as accessors and mutators (or getters and setters). So, the access to the Objects’ data is provided through some methods. These methods work like a bridge (act as an interface) between the object and the outside world, offering controlled access to the object’s data.

Figure 4: Depicting the concept of data abstraction.

Lets take an example code using Abstract Class:

// AbstractPerson.java (Abstract Class)

public abstract class AbstractPerson {
// Protected member variable (accessible to derived classes)
protected int age;

// Public constructor to initialize the age
public AbstractPerson(int initialAge) {
if (initialAge >= 0) {
age = initialAge;
} else {
System.out.println("Invalid age. Setting age to 0.");
age = 0;
}
}

// Abstract method for celebrating birthdays
public abstract void celebrateBirthday();

// Public method to get the age (Accessor or Getter)
public int getAge() {
return age;
}

// Public method to update the age (Mutator or Setter)
public void setAge(int newAge) {
if (newAge >= 0) {
age = newAge;
} else {
System.out.println("Invalid age. Age remains unchanged.");
}
}
}
// ConcretePerson.java (Concrete Class)

public class ConcretePerson extends AbstractPerson {
// Constructor calling the superclass constructor
public ConcretePerson(int initialAge) {
super(initialAge);
}

// Implementation of the abstract method for celebrating birthdays
@Override
public void celebrateBirthday() {
System.out.println("Happy Birthday!");
age++;
}
}
// Main.java

public class Main {
public static void main(String[] args) {
// Creating a ConcretePerson object
ConcretePerson person = new ConcretePerson(25);

// Accessing the age using the getter method
System.out.println("Initial Age: " + person.getAge());

// Updating the age using the setter method
person.setAge(26);
System.out.println("Updated Age: " + person.getAge());

// Performing some action (celebrating birthday)
person.celebrateBirthday();
System.out.println("After Birthday: " + person.getAge());
}
}

Same Example Code but using Interface Class:

// Person.java (Interface)

public interface Person {
// Method to get the age
int getAge();

// Method to set the age
void setAge(int newAge);
}
// BirthdayCelebrator.java (Interface)

public interface BirthdayCelebrator {
// Method for celebrating birthdays
void celebrateBirthday();
}
// ConcretePerson.java (Concrete Class implementing both interfaces)

public class ConcretePerson implements Person, BirthdayCelebrator {
// Private member variable (hidden from the outside world)
private int age;

// Constructor to initialize the age
public ConcretePerson(int initialAge) {
if (initialAge >= 0) {
age = initialAge;
} else {
System.out.println("Invalid age. Setting age to 0.");
age = 0;
}
}

// Implementation of the method to get the age
@Override
public int getAge() {
return age;
}

// Implementation of the method to set the age
@Override
public void setAge(int newAge) {
if (newAge >= 0) {
age = newAge;
} else {
System.out.println("Invalid age. Age remains unchanged.");
}
}

// Implementation of the method for celebrating birthdays
@Override
public void celebrateBirthday() {
System.out.println("Happy Birthday!");
age++;
}
}
// Main.java

public class Main {
public static void main(String[] args) {
// Creating a ConcretePerson object
ConcretePerson person = new ConcretePerson(25);

// Accessing the age using the getter method
System.out.println("Initial Age: " + person.getAge());

// Updating the age using the setter method
person.setAge(26);
System.out.println("Updated Age: " + person.getAge());

// Performing some action (celebrating birthday)
person.celebrateBirthday();
System.out.println("After Birthday: " + person.getAge());
}
}

Note: You’ll observe that the data attributes are placed in the concrete class because an interface class should not contain variables within it. The interface class design purpose is to define contracts and ensures consistent behavior and the abstract class design purpose is to define common behavior and characteristics. I simply want to provide a practical illustration that includes everything we discussed, but in this case, I’d like to emphasize the use of abstract class instead of an interface class.

I suggest using an interface when dealing only with functions and no data members. On the other hand, if your methods involve variables, it’s preferable to implement them using an abstract class. So, as you can see, it’s possible to utilize both approaches.

4.3 — Control or process abstraction

We don’t have to explain every little thing (details) about how an object works. When we keep the inner workings of the different parts that do things for a user a secret (hide the internal implementation of the different functions), it makes a simpler way of understanding what’s happening.

So, In object-oriented programming, control abstraction is the abstraction of actions. Often, we don’t need to provide details about all the methods of an object. In other words, when we hide the internal implementation of the different methods related to the client operation, it creates control abstraction.

Figure 5: Depicting the concept of process abstraction.

Lets take a code example:

Figure 6: illustrate a code example of control abstraction.

4.4 — Example to understand control abstraction and data abstraction together

Suppose we define an abstract data type called Dictionary, where each key is associated with a unique value and we can access values based on their keys. This data structure may be implemented using a hash table, a binary search tree, or even a simple array. For the client code, the abstract properties are the same in each case.

Figure 7: Code snapshot of the dictionary interface, the Hash table, and the binary search tree.
Figure 8: Code snapshot of the client code.

Control abstraction is present in the above example because the client code interacts with the Dictionary interface, which defines the actions that can be performed (putValue and getValue). The client code doesn’t need to know the internal implementation details of the methods in the HashTable and BinarySearchTree classes. In simple terms, it abstracts the control flow of these methods and provides a simple way of accessing and manipulating values in the dictionary.

Data abstraction is also present in the above example because it hides the internal implementation of the data structures. The client code only accesses the data through the defined methods, without knowing how the data is stored internally (e.g., using a HashMap or a binary search tree). In simple terms, the internal implementation of the data structure is abstracted away. This provides a higher-level view and allows flexibility in choosing different data structure implementations without affecting the client code.

5 — Abstraction vs Encapsulation

Abstraction and encapsulation, while frequently employed together in Object-Oriented Programming (OOP), represent distinct concepts with unique meanings. The table below outlines a comparison between abstraction and encapsulation:

Figure 9: Table delineating dissimilarities between encapsulation and abstraction.

Both abstraction and encapsulation contribute to the overall goal of building modular, reusable, and maintainable code in OOP. While abstraction focuses on providing a simplified representation and defining high-level blueprints, encapsulation emphasizes bundling data and methods together within a class, hiding implementation details, and exposing only necessary interfaces for interaction.

6— Abstract classes vs. Interface

Abstraction in Object-Oriented Programming (OOP) can be achieved through either abstract classes or interfaces, each possessing distinct characteristics. The following table provides a comparison of key differences between abstract classes and interfaces.

Figure 10: Table outlining distinctions between an interface and an abstract class.

The decision to use either abstract classes or interfaces relies on the particular design needs (The specific design requirements). Abstract classes are beneficial when code reusability and a shared base implementation (common base implementation) are desired. They prove effective in scenarios (situations) where a group of related classes share common characteristics and behaviors but also have their specific implementations.

On the other hand, interfaces are efficient in establishing agreements (contracts) and enforcing consistent behavior across classes that may not be inherently related (unrelated classes). They are well-suited for situations where diverse and unrelated classes must conform (adhere) to a defined set of methods, allowing for a can-do relationship.

It’s important to consider the design goals and the nature of the relationship between classes when deciding whether to use abstract classes or interfaces in a particular scenario.

7— Points about interface

7.1 —Constants in interface

Remember, if you intend to write code involving all the constants in an interface, that’s not the intended use. Interfaces are designed to create a contract by gathering certain methods as I told you above. It’s important to avoid including data members (properties) within the interface. If you need constants, it’s better to create a final class and place them inside.

Figure 11: Explaining the correct method of utilizing constants.

Remember, if an interface contains a data member, by default, this property will be public, static, and final as shown in the figure below.

Figure 12: Define the default characteristics of a data member within an interface.

7.2 —The convention of giving a name to the interface

The naming convention for interfaces suggests ending the name with ‘able’ or starting it with ‘can’.

Figure 13: The naming convention for interfaces.

7.3 —Default Methods in Interface

In the past, it was impossible to create a function with a body within an interface. However, in the latest version e.g. JDK 1.8 and 1.9, this capability has been introduced, allowing us to take advantage of it as shown in the figure below. The need for such a feature becomes apparent in scenarios where there are numerous classes, each managed by different developers. Imagine a situation where the business team emphasizes the urgency of implementing a critical feature. In such a scenario, you encounter challenges: a multitude of developers handling various classes, all engaged in their respective work. If you were to incorporate a function directly into the interface, each developer would be compelled to invest time in implementing the feature, leading to redundant code. However, by leveraging the option of using default methods in interfaces, you gain the ability to define a function with a body. This approach ensures that all child classes can utilize the function without requiring modifications. Consequently, this not only saves developers’ time but also eliminates the need for redundant code across the child classes.

Figure 14: Understand default methods within an interface.

If you intend to override this function in the child class, you can do so, but ensure to include the access modifier “public” before “void.” If you perform any other actions, an error will be triggered as shown in the figures below.

Figure 15: Incorrect and correct method of overriding a default method.

Please be aware that if you possess two methods in separate interfaces, and a concrete class inherits from both interfaces, an error will occur due to having two methods with identical names and signatures. In such cases, you must modify either the name or the signature of one of the methods as shown in the figure below.

Figure 16: Explain the problem that occurs when two methods with identical signatures are named in two distinct interfaces, and a concrete class inherits from both.

7.4 — private methods in interface

In java 9 version, a new feature is added that allows us to declare private methods inside the interface. The purpose of private methods is just to share some task between the non-abstract methods of the interface.

Figure 17:Specify the characteristics of private methods within an interface.

7.5— Realization Concept

Realization: is a relationship between the blueprint class and the object containing its respective implementation level details. In other words, you can understand this as the relationship between the interface and the implementation class as shown in the figure below.

Figure18: Realization concept.

8— Points about abstract class

8.1 — Why do we use constructor inside abstract class?

If an abstract class contains variables, and considering we can’t create an object of the abstract class, we need to invoke ‘super’ in the concrete class. This ‘super’ refers to the constructor of the abstract class, as illustrated in the example.

Figure 19: Utilizing constructors in an abstract class .

8.2 — When to use abstract methods & abstract class?

Abstract methods are commonly declared when two or more subclasses perform similar tasks in distinct ways with diverse implementations. Subsequently, these subclasses extend the same abstract class and provide different implementations for the abstract methods.

Abstract classes help to describe generic types of behaviors and object-oriented programming class hierarchy. It also describes subclasses to offer implementation details of the abstract class.

8.3 — Rules for Abstract Class

Figure 20: Image illustrating the rules of an abstract class.

8.4- Abstract it is not an access modifier

A class which is declared with the abstraction keyword (Note: it is a non-access modifier) is known as an abstract class in java, it can have abstract and non-abstract methods (method with the body).

🤙 Cheers 🤙

❤️️ I hope this article has helped you understand the concept clearly.❤️️

⭐I’m also available for discussing anything related to the field of software engineering, so if you’d like to chat, feel free to reach out to me on LinkedIn.❤️️

🌟Don’t forget to give a star or leave a comment if you found it useful and Thank you.

--

--