Objects and Classes
Definitions:
Objects: Instances of classes that contain both data and methods that operate on
the data.
Classes: Blueprints for creating objects, providing initial values for state
(attributes) and implementations of behavior (methods).
Use: Define the structure and behavior of objects.
Relation: Central to OOP; interacts with concepts like inheritance, encapsulation,
and polymorphism.
Example: In a student management system, a Student object may have properties like
name, age, and methods like enroll() or submitAssignment().
Code:
# Define a class
class Car:
def __init__(self, model, color):
# Initialize attributes
self.model = model
self.color = color
def drive(self):
# Method to simulate driving
print(f"The {self.color} {self.model} is driving.")
# Create an object
my_car = Car("Toyota", "Red")
# Call the method on the object
my_car.drive() # Output: The Red Toyota is driving.
Encapsulation
Definition: Encapsulation is the bundling of data and methods that operate on the
data within one unit, typically a class, and restricting access to some of the
object's components.
Use: Protects the integrity of the data by preventing outside interference and
misuse.
Relation: Used with classes to hide internal state and require all interaction to
be performed through an object's methods.
Example: Using private variables in a class with public getter and setter methods
to access and modify the variables.
Code:
class Student:
def __init__(self, name, age):
# Private attributes
self.__name = name
self.__age = age
# Public method to get the name
def get_name(self):
return self.__name
# Public method to set the name
def set_name(self, name):
self.__name = name
# Create a Student object
student = Student("Alice", 20)
# Access the private attribute through getter
print(student.get_name()) # Output: Alice
# Modify the private attribute through setter
student.set_name("Bob")
print(student.get_name()) # Output: Bob
Abstraction
Definition: Abstraction is the concept of hiding the complex implementation details
and showing only the necessary features of an object.
Use: Simplifies complex reality by modeling classes appropriate to the problem.
Relation: Works with encapsulation to reduce complexity and increase efficiency.
Example: A Database class with methods connect(), disconnect(), and executeQuery()
without showing the actual connection handling and query execution details.
Code:
from abc import ABC, abstractmethod
# Abstract base class
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass
# Subclass of Animal
class Dog(Animal):
def make_sound(self):
# Implementation of abstract method
print("Bark")
# Subclass of Animal
class Cat(Animal):
def make_sound(self):
# Implementation of abstract method
print("Meow")
# Create objects and call the method
dog = Dog()
dog.make_sound() # Output: Bark
cat = Cat()
cat.make_sound() # Output: Meow
Inheritance
Definition: Inheritance allows a class to inherit properties and methods from
another class.
Use: Promotes code reusability and establishes a natural hierarchy.
Relation: Classes inherit from other classes, supporting polymorphism and code
reuse.
Example: A Bird class might inherit from an Animal class, acquiring attributes like
age and methods like move() from the Animal class.
Code:
# Base class
class Animal:
def __init__(self, name):
self.name = name
def move(self):
# Method to simulate movement
print(f"{self.name} is moving.")
# Derived class inheriting from Animal
class Dog(Animal):
def bark(self):
# Additional method for Dog
print(f"{self.name} is barking.")
# Create a Dog object
dog = Dog("Buddy")
dog.move() # Output: Buddy is moving.
dog.bark() # Output: Buddy is barking.
Generalization
Definition: Generalization is the process of extracting shared characteristics from
two or more classes and combining them into a generalized superclass.
Use: To reduce redundancy by creating a more abstract class.
Relation: Related to inheritance where a general class can be the base for more
specific subclasses.
Example: Creating a Vehicle class from Car and Bike classes that have common
attributes like speed and methods like move().
Code:
# Generalized class
class Vehicle:
def __init__(self, speed):
self.speed = speed
def move(self):
# Method to simulate movement
print(f"Moving at {self.speed} km/h")
# Specialized class inheriting from Vehicle
class Car(Vehicle):
def __init__(self, speed, model):
super().__init__(speed)
self.model = model
def move(self):
# Overriding the move method
print(f"The {self.model} is moving at {self.speed} km/h")
# Create a Car object
car = Car(100, "Toyota")
car.move() # Output: The Toyota is moving at 100 km/h
Polymorphism
Definition: Polymorphism allows methods to do different things based on the object
it is acting upon, even though they share the same name.
Use: To perform a single action in different ways.
Relation: Enables a single interface to represent different underlying forms (data
types).
Example: A draw() method in a Shape class can be implemented differently in
subclasses like Circle, Square, etc.
Code:
# Base class
class Bird:
def fly(self):
# Default implementation
print("Flying in the sky")
# Derived class overriding the method
class Penguin(Bird):
def fly(self):
# Specific implementation
print("Cannot fly, swims instead")
# Demonstrating polymorphism
bird = Bird()
penguin = Penguin()
# Calling the same method on different objects
for animal in (bird, penguin):
animal.fly()
# Output:
# Flying in the sky
# Cannot fly, swims instead
Façade Pattern
Definition: The façade pattern provides a simplified interface to a complex
subsystem.
Use: To reduce the complexity of interaction with a subsystem by providing a single
interface.
Relation: Often used in designing APIs and modular systems.
Example: A Computer class that provides a simple start() method, which internally
calls multiple complex subsystems like CPU, Memory, HardDrive to start the
computer.
Code:
class CPU:
def freeze(self):
print("Freezing CPU")
def jump(self, position):
print(f"Jumping to {position}")
def execute(self):
print("Executing")
class Memory:
def load(self, position, data):
print(f"Loading {data} to {position}")
class HardDrive:
def read(self, lba, size):
return f"Reading {size} bytes from {lba}"
# Facade class
class Computer:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.hard_drive = HardDrive()
def start(self):
# Simplified method to start the computer
self.cpu.freeze()
self.memory.load("0x00", self.hard_drive.read("0x0000", 1024))
self.cpu.jump("0x00")
self.cpu.execute()
# Using the facade
computer = Computer()
computer.start()
Class Diagram Relationships
Definition: Class diagrams in UML show the relationships between classes including
associations, inheritance, dependencies, and aggregations.
Use: To visualize the static structure of a system and how classes relate to one
another.
Relation: Helps in understanding and designing the object-oriented structure of a
system.
Example: A Library class diagram showing relationships with Book, Member, and Loan
classes.
Code:
class Library:
def __init__(self):
self.books = []
def add_book(self, book):
self.books.append(book)
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f"{self.title} by {self.author}"
# Demonstrating relationships
library = Library()
book1 = Book("1984", "George Orwell")
book2 = Book("To Kill a Mockingbird", "Harper Lee")
library.add_book(book1)
library.add_book(book2)
for book in library.books:
print(book)
# Output:
# 1984 by George Orwell
# To Kill a Mockingbird by Harper Lee
Drawing Class Diagrams
Definition: The process of creating visual representations of the classes in a
system and their relationships.
Use: To plan and communicate the structure of a system.
Relation: Involves using UML notation to represent classes, their attributes,
methods, and relationships.
Example: A class diagram for an e-commerce system showing Product, Cart, Order, and
Customer classes with their relationships.
Code:
class User:
def __init__(self, username, email):
self.username = username
self.email = email
def login(self):
# Method to simulate login
print(f"{self.username} logged in")
def logout(self):
# Method to simulate logout
print(f"{self.username} logged out")
# Creating class instances
user1 = User("john_doe", "
[email protected]")
user2 = User("jane_smith", "
[email protected]")
user1.login() # Output: john_doe logged in
user2.logout() # Output: jane_smith logged out
Assessment Task Notification: In-Class Written Test
OOP Process of Design
Definition: The steps involved in creating an object-oriented software system,
including requirements gathering, system design, and implementation.
Use: Provides a structured approach to developing software.
Relation: Involves using principles like encapsulation, inheritance, and
polymorphism.
Example: Designing a payroll system by identifying classes like Employee, Payroll,
and methods like calculateSalary().
Code:
# Define the problem requirements
class PayrollSystem:
def __init__(self):
self.employees = []
def add_employee(self, employee):
self.employees.append(employee)
def calculate_payroll(self):
for employee in self.employees:
print(f'Payroll for: {employee.name} - {employee.calculate_payroll()}')
# Base class
class Employee:
def __init__(self, name):
self.name = name
def calculate_payroll(self):
pass
# Derived class
class SalariedEmployee(Employee):
def __init__(self, name, weekly_salary):
super().__init__(name)
self.weekly_salary = weekly_salary
def calculate_payroll(self):
return self.weekly_salary
# Using the design process
payroll_system = PayrollSystem()
john = SalariedEmployee("John Smith", 1500)
payroll_system.add_employee(john)
payroll_system.calculate_payroll()
# Output: Payroll for: John Smith - 1500
OOP Coding Techniques
Definition: Best practices and strategies for writing efficient and maintainable
object-oriented code.
Use: Ensures the development of robust, scalable, and readable code.
Relation: Involves using coding standards, design patterns, and refactoring
techniques.
Example: Implementing the Singleton pattern to ensure only one instance of a
Database connection exists.
Code:
class DatabaseConnection:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, database_url):
self.database_url = database_url
def connect(self):
print(f"Connecting to {self.database_url}")
# Demonstrating Singleton pattern
db1 = DatabaseConnection("db1_url")
db2 = DatabaseConnection("db2_url")
db1.connect() # Output: Connecting to db1_url
db2.connect() # Output: Connecting to db1_url (same instance)