Introduction to Object-Oriented Programming
class Car:
def __init__(self, color, model, year):
self.color = color
self.model = model
self.year = year
def accelerate(self):
print("The car is accelerating!")
def brake(self):
print("The car is braking!")
def honk(self):
print("Beep beep!")
# Creating an instance (object) of the Car class
my_car = Car("red", "Sedan", 2022)
# Accessing properties and calling methods
print(my_car.color) # Output: red
print(my_car.model) # Output: Sedan
print(my_car.year) # Output: 2022
my_car.accelerate() # Output: The car is accelerating!
my_car.honk() # Output: Beep beep!
Check Out!
Object-oriented programming principles could be misused or violated which could have several consequences, including reduced code quality, increased complexity, decreased maintainability, decreased performance, and increased debugging effort.
Benefits of Object-Oriented Programming
1. Classes and Objects
class Dog:
def __init__(self, name, breed, age):
self.name = name
self.breed = breed
self.age = age
def bark(self):
print("Woof! Woof!")
def eat(self):
print("The dog is eating.")
def sleep(self):
print("The dog is sleeping.")
# Creating a dog object (instance of the Dog class)
my_big_dog = Dog("Buddy", "Labrador", 3)
my_little_dog = Dog("Tiny", "Chihuahua", 1)
# Accessing properties and calling methods of my_big_dog
print(my_big_dog.name) # Output: Buddy
print(my_big_dog.breed) # Output: Labrador
print(my_big_dog.age) # Output: 3
my_big_dog.bark() # Output: Woof! Woof!
my_big_dog.eat() # Output: The dog is eating.
my_big_dog.sleep() # Output: The dog is sleeping.
# Accessing properties and calling methods of my_little_dog
print(my_little_dog.name) # Output: Tiny
print(my_little_dog.breed) # Output:Chihuahua
print(my_little_dog.age) # Output: 1
my_little_dog.bark() # Output: Woof! Woof!
my_little_dog.eat() # Output: The dog is eating.
my_little_dog.sleep() # Output: The dog is sleeping.
Properties | my_big_dog | my_little_dog |
---|---|---|
name | Buddy | Tiny |
breed | Labrador | Chihuahua |
age | 3 | 1 |
Using classes and objects in programming allows us to create and manipulate different instances of the same type, each with their own unique data and behavior. This helps in organizing code, making it easier to work with and reuse.
-- Tip
2. Encapsulation: Keeping Things Together
By encapsulating data, you ensure data integrity and protect it from unauthorized access. Encapsulation promotes code modularity and improves the overall maintainability of the software.
-- Tip
3. Inheritance: Reusing and Extending Code
class Vehicle:
def start_engine(self):
print("Engine started.")
class Car(Vehicle):
def drive(self):
print("Car is being driven.")
class Motorcycle(Vehicle):
def wheelie(self):
print("Motorcycle is doing a wheelie.")
# Creating objects from the derived classes
my_car = Car()
my_motorcycle = Motorcycle()
# Using the inherited methods
my_car.start_engine() # Output: Engine started.
my_car.drive() # Output: Car is being driven.
my_motorcycle.start_engine() # Output: Engine started.
my_motorcycle.wheelie() # Output: Motorcycle is doing a wheelie.
Inheritance allows us to reuse code efficiently. We can create a hierarchy of classes, with each level adding more specific properties and behaviors. This way, we can build complex systems step by step, without having to rewrite common functionality.
-- Tip
4. Polymorphism: Flexibility and Dynamic Behavior
class Shape:
def calculate_area(self):
pass # Placeholder for the actual implementation
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def calculate_area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def calculate_area(self):
return self.width * self.height
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def calculate_area(self):
return 0.5 * self.base * self.height
# Creating objects from the derived classes
my_circle = Circle(5)
my_rectangle = Rectangle(4, 3)
my_triangle = Triangle(6, 2)
# Using the common method
print(my_circle.calculate_area()) # Output: 78.5
print(my_rectangle.calculate_area()) # Output: 12
print(my_triangle.calculate_area()) # Output: 6
Polymorphism allows us to write generic code that can work with objects of different types, as long as they share a common superclass. It enhances flexibility and modularity, enabling code to be written in a more generic and extensible manner.
-- Tip
5. Abstraction
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass
class Dog(Animal):
def make_sound(self):
print("Woof! Woof!")
class Cat(Animal):
def make_sound(self):
print("Meow!")
# Creating objects from the derived classes
my_dog = Dog()
my_cat = Cat()
# Using the abstract method
my_dog.make_sound() # Output: Woof! Woof!
my_cat.make_sound() # Output: Meow!
The abstract keyword indicates that this class cannot be instantiated directly, and its methods must be implemented by the derived classes.
Abstraction helps in building modular and loosely coupled systems by focusing on the essential features and hiding unnecessary details. It simplifies complex systems and allows us to work with higher-level concepts without getting caught up in the implementation specifics.
-- Tip
6. Association
class Person:
def __init__(self, name):
self.name = name
self.car = None
def assign_car(self, car):
self.car = car
def drive_car(self):
if self.car is not None:
print(self.name, "is driving the", self.car.brand)
else:
print(self.name, "doesn't have a car.")
class Car:
def __init__(self, brand):
self.brand = brand
# Creating objects from the classes
person1 = Person("John")
person2 = Person("Emily")
car1 = Car("Toyota")
# Associating objects
person1.assign_car(car1)
person2.assign_car(car1)
# Using the associated objects
person1.drive_car() # Output: John is driving the Toyota
person2.drive_car() # Output: Emily is driving the Toyota
7. Composition
class Engine:
def start(self):
print("Engine started.")
def stop(self):
print("Engine stopped.")
class Wheel:
def rotate(self):
print("Wheel rotating.")
class Car:
def __init__(self):
self.engine = Engine()
self.wheels = [Wheel() for _ in range(4)]
def start_car(self):
self.engine.start()
for wheel in self.wheels:
wheel.rotate()
def stop_car(self):
self.engine.stop()
for wheel in self.wheels:
print("Wheel stopped.")
# Creating a car object
my_car = Car()
# Starting and stopping the car
my_car.start_car() # Output: Engine started. Wheel rotating. (4 times)
my_car.stop_car() # Output: Engine stopped. Wheel stopped. (4 times)
8. Aggregation
class Student:
def __init__(self, name):
self.name = name
class University:
def __init__(self, name):
self.name = name
self.students = []
def add_student(self, student):
self.students.append(student)
def remove_student(self, student):
self.students.remove(student)
# Creating student objects
student1 = Student("John")
student2 = Student("Emily")
# Creating a university object
my_university = University("ABC University")
# Adding students to the university
my_university.add_student(student1)
my_university.add_student(student2)
# Removing a student from the university
my_university.remove_student(student1)
# Printing the remaining students in the university
for student in my_university.students:
print(student.name) # Output: Emily
9. Design Patterns
class President:
__instance = None
@staticmethod
def get_instance():
if President.__instance is None:
President.__instance = President()
return President.__instance
def __init__(self):
if President.__instance is not None:
raise Exception("Only one president can exist.")
else:
President.__instance = self
def speak(self):
print("I am the president.")
# Creating instances of the President class
president1 = President.get_instance()
president2 = President.get_instance()
president1.speak() # Output: I am the president.
president2.speak() # Output: I am the president.
Using the Singleton pattern ensures that no matter how many times we try to create an instance of the "President" class, we always get the same instance. Just like in the real world, there can only be one president of a country at a time
By utilizing design patterns like Singleton, you can solve common design problems in a structured and proven manner. Understanding and applying these patterns in your software development journey will greatly enhance your skills and improve the quality of your code.
-- Tip
Conclusion
Learn more about OOP
- freecodecamp: [OOP Meaning – What is Object-Oriented Programming?]
- geeksforgeeks: [Introduction of Object Oriented Programming]
- programiz : [Python Object Oriented Programming]