Demystifying Object-Oriented Programming(OOP): A Beginner’s Guide

OOP stands for Object-Oriented Programming, which is a programming paradigm that focuses on organizing software design around objects. In OOP, an object is an instance of a class, which encapsulates data and behavior (methods) into a single entity.
The four fundamental principles of OOP are:
Encapsulation: This refers to the practice of hiding the internal details of an object and exposing only the necessary functionality through a public interface. This helps to reduce complexity, improve security, and increase modularity.
Inheritance: This allows classes to inherit properties and methods from parent classes. It helps to create a hierarchy of classes and makes code more reusable.
Polymorphism: This refers to the ability of objects to take on many forms. Polymorphism allows classes to have multiple methods with the same name but different parameters, which makes code more flexible and easier to maintain.
Abstraction: This refers to the practice of defining objects in terms of their essential characteristics, rather than their implementation details. Abstraction helps to reduce complexity and improve code readability.
OOP is widely used in software development because it allows for modular and reusable code, which makes it easier to develop, test, and maintain software systems.
Encapsulation
Okay, let’s imagine that you have a treasure chest with lots of cool toys inside it. You want to show your friends some of the toys, but you don’t want them to take everything out of the chest and make a big mess. So, what do you do?
You might decide to put a lock on the chest, and only give the key to your friends when you want them to play with the toys. This way, your friends can only access the toys you want them to play with, and they can’t mess up the rest of the toys in the chest.
In computer programming, we do something similar with encapsulation. We put a “lock” around the data and methods in an object, and only allow certain parts of the code to access them. This helps to keep the code organized, and prevents other parts of the code from accidentally changing things they shouldn’t.
So, just like you protect your treasure chest by locking it up, programmers protect their code by using encapsulation!
Code Example:
public class BankAccount {
private double balance;
private String accountNumber;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
balance -= amount;
}
public String getAccountNumber() {
return accountNumber;
}
}
In this example, we have a BankAccount class with two instance variables, balance and accountNumber. Notice that both of these variables are marked as private. This means that they can only be accessed within the BankAccount class itself, and not from other parts of the code.
To allow other parts of the code to access these variables in a controlled way, we provide two public methods: getBalance() and getAccountNumber(). These methods allow other parts of the code to get information about the BankAccount object, but they can’t change the values of the balance or accountNumber variables directly.
We also provide two other methods, deposit() and withdraw(), which allow other parts of the code to modify the balance variable, but only in a controlled way. These methods ensure that the balance variable is only changed by authorized code, and that the value of the balance variable is always valid (e.g. it can’t go negative).
By using encapsulation in this way, we can ensure that the BankAccount object is always in a valid state, and that other parts of the code can’t accidentally break the rules.
Here’s an example of a main program that uses the BankAccount class I showed earlier:
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount("1234", 1000.0);
System.out.println("Account number: " + account.getAccountNumber());
System.out.println("Initial balance: " + account.getBalance());
account.deposit(500.0);
System.out.println("New balance after deposit: " + account.getBalance());
account.withdraw(200.0);
System.out.println("New balance after withdrawal: " + account.getBalance());
}
}
In this example, we create a BankAccount object with an initial balance of $1000. We then print out the account number and initial balance using the getAccountNumber() and getBalance() methods.
We then deposit $500 into the account using the deposit() method, and print out the new balance using getBalance(). Finally, we withdraw $200 from the account using the withdraw() method, and print out the new balance again.
This program demonstrates how we can use the BankAccount class to manage a bank account in a controlled way, using encapsulation to protect the internal data from unauthorized access or modification.
Inheritance
Imagine you have a big box of crayons. There are different colors in there, like red, blue, green, and yellow. But there are also different types of crayons, like regular crayons, glitter crayons, and glow-in-the-dark crayons.
Now imagine that your friend has a box of crayons too, but they only have regular crayons. They see your glitter and glow-in-the-dark crayons and they want to use them too.
That’s kind of like what inheritance is in programming. One class (or box of crayons) can “inherit” properties and methods from another class (or box of crayons). This means that the new class (or box of crayons) has everything the old class (or box of crayons) has, plus some extra stuff.
Just like your friend can use your glitter and glow-in-the-dark crayons because they’re part of your collection, a new class can use the properties and methods of an existing class because they inherit them. This can save time and make programming easier, just like sharing crayons with a friend!
Here’s an example of inheritance using the crayon analogy:
public class Crayon {
private String color;
public Crayon(String color) {
this.color = color;
}
public void draw() {
System.out.println("Drawing with " + color + " crayon.");
}
}
public class GlitterCrayon extends Crayon {
public GlitterCrayon(String color) {
super(color);
}
public void sparkle() {
System.out.println(color + " glitter crayon is sparkling!");
}
}
public class GlowInTheDarkCrayon extends Crayon {
public GlowInTheDarkCrayon(String color) {
super(color);
}
public void glow() {
System.out.println(color + " glow-in-the-dark crayon is glowing!");
}
}
public class Main {
public static void main(String[] args) {
GlitterCrayon glitterCrayon = new GlitterCrayon("pink");
GlowInTheDarkCrayon glowCrayon = new GlowInTheDarkCrayon("green");
glitterCrayon.draw();
glitterCrayon.sparkle();
glowCrayon.draw();
glowCrayon.glow();
}
}
In this example, we have a Crayon class that represents a generic crayon with a color. The Crayon class has one method: draw(), which prints out a message indicating that the crayon is being used to draw.
We then create two subclasses, GlitterCrayon and GlowInTheDarkCrayon, that extend the Crayon class using the extends keyword. This means that both subclasses inherit the color property and the draw() method from the Crayon class.
In addition to the inherited properties and methods, the GlitterCrayon subclass adds a sparkle() method that prints out a message indicating that the glitter crayon is sparkling, while the GlowInTheDarkCrayon subclass adds a glow() method that prints out a message indicating that the glow-in-the-dark crayon is glowing.
Finally, in the Main class, we create a GlitterCrayon object and a GlowInTheDarkCrayon object, and call the draw(), sparkle(), and glow() methods on each of them, depending on their specific properties. This program demonstrates how inheritance allows subclasses to inherit and extend properties and methods from their superclass, just like sharing different types of crayons from the same box.
Polymorphism
Polymorphism is a big word that means “many shapes”. It’s like having a toy that can change into different shapes and do different things depending on what shape it is.
In programming, polymorphism means that we can use the same code to work with different types of things. For example, we can have a method that can work with different types of animals, like a dog, a cat, or a bird.
So just like a toy that can change into different shapes and do different things, in programming, we can use the same code to work with different things that have something in common.
Here’s an example of polymorphism using Java:
public class Animal {
public void makeSound() {
System.out.println("The animal makes a sound");
}
}
public class Dog extends Animal {
public void makeSound() {
System.out.println("The dog barks");
}
}
public class Cat extends Animal {
public void makeSound() {
System.out.println("The cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Animal();
Animal animal2 = new Dog();
Animal animal3 = new Cat();
animal1.makeSound();
animal2.makeSound();
animal3.makeSound();
}
}
In this example, we have a Animal class that represents a generic animal with a makeSound() method that prints out a message indicating that the animal makes a sound.
We then create two subclasses, Dog and Cat, that extend the Animal class and override the makeSound() method to print out messages indicating that the dog barks and the cat meows, respectively.
Finally, in the Main class, we create three objects, one of type Animal, one of type Dog, and one of type Cat, and call the makeSound() method on each of them. Since the Dog and Cat classes override the makeSound() method, they will print out their own specific messages when the method is called on them.
This demonstrates polymorphism in action, as we are able to use the same method makeSound() on different types of animals and get different results based on the specific type of animal.
Abstraction
Abstraction is like a picture of a person or a thing that shows only the important parts. Just like how a cartoon of a dog only shows the important features like its tail, ears, and nose, but leaves out the details of its fur, claws, or tongue.
In programming, abstraction means we can create a simplified version of something by hiding its complex details so that we can focus on what’s important for our program to work. For example, if we’re making a game with different characters, we can create an abstract class called Character that has the important features that all characters share, like their health, their position on the screen, and how they move around, but we don’t have to worry about the details of each character’s appearance, weapons, or special abilities.
So, just like how a cartoon shows only the important parts of a person or a thing, in programming we can use abstraction to simplify things and focus on what’s important for our program to work.
Here’s an example of abstraction using Java:
abstract class Shape {
int x, y;
public Shape(int x, int y) {
this.x = x;
this.y = y;
}
public abstract void draw();
}
class Circle extends Shape {
int radius;
public Circle(int x, int y, int radius) {
super(x, y);
this.radius = radius;
}
public void draw() {
System.out.println("Drawing a circle at (" + x + ", " + y + ") with radius " + radius);
}
}
class Square extends Shape {
int side;
public Square(int x, int y, int side) {
super(x, y);
this.side = side;
}
public void draw() {
System.out.println("Drawing a square at (" + x + ", " + y + ") with side " + side);
}
}
public class Main {
public static void main(String[] args) {
Shape shape1 = new Circle(5, 10, 20);
Shape shape2 = new Square(15, 20, 30);
shape1.draw();
shape2.draw();
}
}
In this example, we have an abstract Shape class that represents a generic shape with an x and y coordinate and a draw() method. The draw() method is declared as abstract, which means that any class that extends Shape must provide its own implementation of the draw() method.
We then create two subclasses, Circle and Square, that extend the Shape class and implement their own draw() method to draw a circle and a square, respectively.
Finally, in the Main class, we create two objects, one of type Circle and one of type Square, and assign them to variables of type Shape. Since both Circle and Square are subclasses of Shape, we can treat them as Shape objects, which allows us to call the draw() method on them.
This demonstrates abstraction in action, as we are able to create a simplified version of a shape with only the important features, and then create more specific shapes by extending the Shape class and providing our own implementation of the draw() method. By using abstraction, we can focus on what’s important for our program to work and leave out the complex details.
Final Words
In conclusion, OOP may seem daunting at first, but it’s actually a lot of fun! Once you get the hang of it, you’ll start to see the power and flexibility that OOP can bring to your code. Encapsulation, inheritance, polymorphism, and abstraction may sound like big, fancy words, but they’re really just tools that help you write better, more organized, and more maintainable code.
So don’t be afraid to dive in and start playing around with OOP concepts! Experiment with different class hierarchies, try out new inheritance relationships and see how polymorphism can make your code more flexible. You may be surprised at what you can create when you harness the power of OOP.
Remember, programming is all about creativity and problem-solving. OOP gives you the tools you need to express your creativity and tackle problems in new and innovative ways. So go forth, write some awesome code, and have fun along the way!