Practical 12: Object Oriented Programming Principles
Objective
In this lab, you will implement Basic Inheritance, Abstraction, Polymorphism and SOLID Principles in exercises. This exercise will help you understand the concepts.
Submission Date:
Prerequisites
- Basic knowledge of Python syntax
- Understanding of lists and functions in Python
- Familiarity with time complexity concepts (optional, but helpful)
Lab Steps
Step 1: Basic Inheritance
Employee is subclass of Person. super() function is used to call the parent class’s methods, and leverage the functionality of the parent class.
# Parent Class: Person
class Person:
def __init__(self, name, idnumber):
self.name = name
self.idnumber = idnumber
def display(self):
print(self.name)
print(self.idnumber)
# Child Class: Employee
class Employee(Person):
def __init__(self, name, idnumber, salary, post):
super().__init__(name, idnumber)
# Using super() to call Person's __init__()
self.salary = salary
self.post = post
employee1 = Employee("Sonam", 22404, 60000, "student")
employee1.display()
Step 2: Data Abstraction
- Create abstract base class Car
- Import required modules
- Create child class Hatchback
from abc import ABC, abstractmethod
class Car(ABC):
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
# Create abstract method
@abstractmethod
def printDetails(self):
pass
# Create concrete method
def accelerate(self):
print("Speed up ...")
class Hatchback(Car):
def printDetails(self):
print("Brand:", self.brand)
print("Model:", self.model)
print("Year:", self.year)
def sunroof(self):
print("Not having this feature")
# car1 = Car("Toyota", "Prius", "2004") # This does not work
hatch1 = Hatchback("Toyota", "Prius", "2004")
hatch1.sunroof()
Step 3: Class Polymorphism
Runtime polymorphism is achieved through method overriding. The study() method exhibits polymorphic behavior based on the object calling it.
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def study(self):
print(self.name, " is studying Python.")
class HighSchoolStudent(Student):
def __init__(self, name, age, grade):
super().__init__(name, age)
self.grade = grade
def study(self):
print(self.name, " is studying Mathematics.")
Karma = Student("Karma", 25)
Karma.study()
Sonam = HighSchoolStudent("Sonam Lhamo", 20, 12)
Sonam.study()
Step 4: Function Polymorphism
Methods can be written to have different behaviour depending of type of inputs received There are some functions in Python which are compatible to run with multiple data types. One such function is the len() function. It can run with many data types in Python. Let's look at some example use cases of the function
# Length of String
print(len("Programiz"))
# Length of List
print(len(["Python", "Java", "C"]))
# Length of Dictionary
print(len({"Name": "John", "Address": "Nepal"}))
Step 5: Operator Polymorphism
This is the ability to use common operations in different forms for different data inputs. Operator Overloading means giving extended meaning beyond their predefined operational meaning.
class complex:
def __init__(self, a, b):
self.a = a
self.b = b
# adding two objects
def __add__(self, other):
return self.a + other.a, self.b + other.b
Ob1 = complex(1, 2)
Ob2 = complex(2, 3)
Ob3 = Ob1 + Ob2
print(type(Ob3), Ob3)
Step 6: SOLID Principle 1: Single Responsibility Principle (SRP)
“A class should have one and only one reason to change.” – Robert C. Martin
class Person:
def __init__(self, name):
self.name = name
def __repr__(self):
return f'Person(name={self.name})'
class PersonDB:
def save(self, person):
print(f'Save the {person} to database')
Step 7: SOLID Principle 2: Open/Closed Principle (OCP)
“Software entities should be open for extension, but closed for modification.” – Bertrand Meyer
from abc import ABC, abstractmethod
class PersonStorage(ABC):
@abstractmethod
def save(self, person):
pass
class PersonDB(PersonStorage):
def save(self, person):
print(f'Save the {person} to database')
class PersonJSON(PersonStorage):
def save(self, person):
print(f'Save the {person} to JSON file')
Step 8: SOLID Principle 3: Liskov Substitution Principle (LSP)
“Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.” – Barbara Liskov
from abc import ABC, abstractmethod
class Notification(ABC):
@abstractmethod
def notify(self, message):
pass
class Email(Notification):
def __init__(self, email):
self.email = email
def notify(self, message):
print(f'Send "{message}" to {self.email}')
class SMS(Notification):
def __init__(self, phone):
self.phone = phone
def notify(self, message):
print(f'Send "{message}" to {self.phone}')
class Contact:
def __init__(self, name, email, phone):
self.name = name
self.email = email
self.phone = phone
class NotificationManager:
def __init__(self, notification):
self.notification = notification
def send(self, message):
self.notification.notify(message)
if __name__ == '__main__':
contact = Contact('John Doe', 'john@test.com', '(408)-888-9999')
sms_notification = SMS(contact.phone)
email_notification = Email(contact.email)
notification_manager = NotificationManager(sms_notification)
notification_manager.send('Hello John')
notification_manager.notification = email_notification
notification_manager.send('Hi John')
Step 9: SOLID Principle 4: Interface Segregation Principle (ISP)
“Clients should not be forced to depend on methods they do not use.” – Robert C. Martin
First, split the Vehicle interface into two smaller interfaces: Movable and Flyable, and inherits the Movable class from the Flyable class. Second, inherits from the Flyable class from the Aircraft class. Third, inherit the Movable class from the Car class.
class Movable(ABC):
@abstractmethod
def go(self):
pass
class Flyable(Movable):
@abstractmethod
def fly(self):
pass
class Aircraft(Flyable):
def go(self):
print("Taxiing")
def fly(self):
print("Flying")
class Car(Movable):
def go(self):
print("Going")
Step 10: SOLID Principle 5: Dependency Inversion Principle (DIP)
“High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.” – Robert C. Martin
CurrencyConverter is the abstraction layer
from abc import ABC
class CurrencyConverter(ABC):
def convert(self, from_currency, to_currency, amount) -> float:
pass
class FXConverter(CurrencyConverter):
def convert(self, from_currency, to_currency, amount) -> float:
print('Converting currency using FX API')
print(f'{amount} {from_currency} = {amount * 1.15} {to_currency}')
return amount * 1.15
class AlphaConverter(CurrencyConverter):
def convert(self, from_currency, to_currency, amount) -> float:
print('Converting currency using Alpha API')
print(f'{amount} {from_currency} = {amount * 1.2} {to_currency}')
return amount * 1.2
class App:
def __init__(self, converter: CurrencyConverter):
self.converter = converter
def start(self):
self.converter.convert('EUR', 'USD', 100)
if __name__ == '__main__':
converter = AlphaConverter() # Select converter here
app = App(converter)
app.start()
Step 11: Run through OOP unit Worksheet 2
Run through OOP unit Worksheet 2 on Online Banking Application
Exercises for Students
- Implement various different variables, methods and classes based on principles of Basic Inheritance, Abstraction, Polymorphism. and Solid Principles.
Conclusion
In this lab, you've implemented and tested further concepts on OOP.