SOLID Principles Clean Code & Object Oriented Design
Introduction
The SOLID principles are a set of five design rules that help developers write clean, maintainable, and scalable code in object-oriented programming (OOP). These principles are widely used in professional software development and are essential for mastering clean code and object oriented design principles.
For Pakistani students studying computer science in universities like those in Lahore, Karachi, and Islamabad, learning SOLID principles can significantly improve coding skills and job readiness. Whether you're building a university project, a freelancing application, or preparing for software engineering interviews, SOLID helps you write code that is easier to understand, modify, and extend.
In simple terms, SOLID principles help you:
- Avoid messy, hard-to-maintain code
- Build flexible and reusable software
- Work better in team-based development environments
Prerequisites
Before starting this solid principles tutorial, you should have:
- Basic understanding of Object-Oriented Programming (OOP)
- Familiarity with any programming language (Python, Java, or C# preferred)
- Knowledge of:
- Classes and objects
- Inheritance
- Functions/methods
Core Concepts & Explanation
Single Responsibility Principle (SRP)
Definition: A class should have only one reason to change.
This means a class should perform only one task or responsibility.
Bad Example (SRP Violation):
class Student:
def calculate_fee(self):
return 50000
def print_receipt(self):
print("Fee receipt printed")
Problem:
The Student class is handling both business logic (fee calculation) and presentation logic (printing receipt).
Good Example (SRP Applied):
class Student:
def calculate_fee(self):
return 50000
class ReceiptPrinter:
def print_receipt(self):
print("Fee receipt printed")
Explanation:
Studentclass handles only student-related logicReceiptPrinterhandles printing- Each class has a single responsibility
Open/Closed Principle (OCP)
Definition: Software should be open for extension but closed for modification.
You should be able to add new functionality without changing existing code.
Example:
class Discount:
def get_discount(self, customer_type):
if customer_type == "student":
return 10
elif customer_type == "teacher":
return 20
Problem:
If a new type (e.g., "freelancer") is added, you must modify this class.
Better Approach:
class Discount:
def get_discount(self):
pass
class StudentDiscount(Discount):
def get_discount(self):
return 10
class TeacherDiscount(Discount):
def get_discount(self):
return 20
Explanation:
- New discount types can be added without modifying existing classes
- Code is easier to extend
Liskov Substitution Principle (LSP)
Definition: Subclasses should be replaceable with their parent class without breaking functionality.
Bad Example:
class Bird:
def fly(self):
print("Flying")
class Penguin(Bird):
def fly(self):
raise Exception("Penguins can't fly")
Problem:Penguin breaks the expected behavior of Bird.
Better Approach:
class Bird:
pass
class FlyingBird(Bird):
def fly(self):
print("Flying")
class Penguin(Bird):
def swim(self):
print("Swimming")
Explanation:
- Only birds that can fly inherit from
FlyingBird - No unexpected behavior
Interface Segregation Principle (ISP)
Definition: Clients should not be forced to depend on methods they do not use.
Bad Example:
class Worker:
def work(self):
pass
def eat(self):
pass
Problem:
A robot worker does not need eat().
Better Approach:
class Workable:
def work(self):
pass
class Eatable:
def eat(self):
pass
Explanation:
- Separate interfaces for different responsibilities
- Classes implement only what they need
Dependency Inversion Principle (DIP)
Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions.
Bad Example:
class MySQLDatabase:
def connect(self):
print("Connected to MySQL")
class App:
def __init__(self):
self.db = MySQLDatabase()
Problem:App is tightly coupled to MySQLDatabase.
Better Approach:
class Database:
def connect(self):
pass
class MySQLDatabase(Database):
def connect(self):
print("Connected to MySQL")
class App:
def __init__(self, db: Database):
self.db = db
Explanation:
Appdepends on abstraction (Database)- You can switch to PostgreSQL easily

Practical Code Examples
Example 1: Student Fee Management System
class FeeCalculator:
def calculate_fee(self, base_fee):
return base_fee + 5000 # additional charges
class Discount:
def apply_discount(self, fee):
return fee * 0.9 # 10% discount
class Receipt:
def generate(self, fee):
print(f"Total Fee: {fee} PKR")
# Usage
calculator = FeeCalculator()
discount = Discount()
receipt = Receipt()
fee = calculator.calculate_fee(50000)
discounted_fee = discount.apply_discount(fee)
receipt.generate(discounted_fee)
Line-by-line Explanation:
FeeCalculator: Calculates student feeDiscount: Applies discount logicReceipt: Handles output- Objects are created separately → follows SRP
- Code is modular and reusable
Example 2: Real-World Application (E-Commerce in Pakistan)
Imagine Ahmad runs an online store in Karachi.
class PaymentMethod:
def pay(self, amount):
pass
class JazzCash(PaymentMethod):
def pay(self, amount):
print(f"Paid {amount} PKR via JazzCash")
class EasyPaisa(PaymentMethod):
def pay(self, amount):
print(f"Paid {amount} PKR via EasyPaisa")
class Order:
def __init__(self, payment_method: PaymentMethod):
self.payment_method = payment_method
def checkout(self, amount):
self.payment_method.pay(amount)
# Usage
order = Order(JazzCash())
order.checkout(2000)
Line-by-line Explanation:
PaymentMethod: Abstract classJazzCashandEasyPaisa: Concrete implementationsOrder: Depends on abstraction → DIP- Easy to add new payment methods (OCP)

Common Mistakes & How to Avoid Them
Mistake 1: Creating God Classes
Problem: One class does everything.
Fix:
# Split responsibilities into multiple classes
Tip: Always ask: “Does this class have more than one responsibility?”
Mistake 2: Overusing Inheritance
Problem: Deep inheritance hierarchy makes code complex.
Fix: Use composition instead.
class Engine:
def start(self):
print("Engine started")
class Car:
def __init__(self):
self.engine = Engine()
Explanation:
CarusesEngineinstead of inheriting from it

Practice Exercises
Exercise 1: Refactor Student Class
Problem:
A class handles both student data and printing.
Solution:
class Student:
def get_name(self):
return "Ali"
class Printer:
def print_name(self, name):
print(name)
Explanation:
- Responsibilities separated → SRP applied
Exercise 2: Add New Payment Method
Problem: Add “Bank Transfer” without modifying existing code.
Solution:
class BankTransfer(PaymentMethod):
def pay(self, amount):
print(f"Paid {amount} PKR via Bank Transfer")
Explanation:
- New functionality added without changing old code → OCP
Frequently Asked Questions
What is SOLID in programming?
SOLID is a set of five object-oriented design principles that help developers write clean, maintainable, and scalable code. These principles improve code structure and flexibility.
Why are SOLID principles important?
They make code easier to understand, test, and extend. In real-world software development, especially in team projects, SOLID principles reduce bugs and improve collaboration.
How do I apply SOLID principles in projects?
Start by designing small classes with single responsibilities, use interfaces or abstractions, and avoid tightly coupled code. Practice regularly with real-world examples.
Are SOLID principles only for large projects?
No, they are useful for both small and large projects. Even in university assignments, applying SOLID principles improves code quality.
Which programming languages support SOLID?
SOLID principles can be applied in any object-oriented language such as Python, Java, C++, and C#.
Summary & Key Takeaways
- SOLID principles improve clean code and maintainability
- Each class should have a single responsibility (SRP)
- Code should be extendable without modification (OCP)
- Subclasses should behave like their parent classes (LSP)
- Avoid forcing classes to implement unnecessary methods (ISP)
- Use abstractions to reduce dependency (DIP)
Next Steps & Related Tutorials
To deepen your understanding, explore these tutorials on theiqra.edu.pk:
- Learn advanced Design Patterns in Software Engineering
- Master Python OOP Concepts for Beginners to Advanced
- Explore Clean Code Best Practices for Developers
- Understand Software Architecture Fundamentals
By practicing these concepts regularly, you’ll move closer to becoming a professional software engineer ready for the Pakistani and global tech industry 🚀
Test Your Python Knowledge!
Finished reading? Take a quick quiz to see how much you've learned from this tutorial.