In scope: Explanation and demonstration of the five main OOD principles (SRP, OCP, LSP, ISP, DIP) with simple examples. Out of scope: Detailed language-specific syntax or advanced design patterns.
Functional Requirements
FR1: Explain the core principles of object-oriented design (OOD).
FR2: Demonstrate how these principles improve software design quality.
FR3: Show examples of applying these principles in a simple system.
FR4: Ensure the design supports easy maintenance and scalability.
Non-Functional Requirements
NFR1: Design must be understandable by beginners.
NFR2: Use simple, relatable examples.
NFR3: Focus on principles, not specific programming languages.
NFR4: Avoid complex jargon or advanced patterns.
Think Before You Design
Questions to Ask
❓ Question 1
❓ Question 2
❓ Question 3
❓ Question 4
Key Components
Classes and objects
Interfaces or abstract classes
Modules or packages
Dependencies between components
Design Patterns
Single Responsibility Principle (SRP)
Open/Closed Principle (OCP)
Liskov Substitution Principle (LSP)
Interface Segregation Principle (ISP)
Dependency Inversion Principle (DIP)
Reference Architecture
+---------------------+ +---------------------+
| Client | | Service Layer |
+---------------------+ +---------------------+
| |
| uses | depends on
v v
+---------------------+ +---------------------+
| Interface / API |<----->| Implementation Class|
+---------------------+ +---------------------+
^ |
| |
depends on depends on
| |
+---------------------+ +---------------------+
| Abstract Classes | | Concrete Classes |
+---------------------+ +---------------------+
Components
Client
Conceptual
Uses the system through interfaces, demonstrating dependency inversion.
Interface / API
Abstract Interface
Defines contracts for classes, supporting ISP and DIP.
Implementation Class
Concrete Class
Implements interfaces, following SRP and OCP.
Abstract Classes
Abstract Base Classes
Provide base functionality and support LSP.
Concrete Classes
Concrete Classes
Extend abstract classes and implement interfaces, adhering to all principles.
Request Flow
1. 1. Client calls a method on an interface, not a concrete class (DIP).
2. 2. The interface defines the expected behavior (ISP).
3. 3. Concrete classes implement the interface, each with a single responsibility (SRP).
4. 4. New features are added by extending classes without modifying existing code (OCP).
5. 5. Subtypes can replace base types without breaking the system (LSP).
Database Schema
Not applicable as this is a design principles concept focusing on class relationships and dependencies.
Scaling Discussion
Bottlenecks
Tight coupling between classes making changes risky.
Large classes violating SRP causing maintenance issues.
Interfaces that are too broad violating ISP.
Inheritance hierarchies that break LSP causing unexpected behavior.
Direct dependencies on concrete classes violating DIP.
Solutions
Use interfaces and abstractions to reduce coupling.
Split classes to ensure each has a single responsibility.
Create focused interfaces tailored to client needs.
Design inheritance carefully to ensure substitutability.
Depend on abstractions rather than concrete implementations.
Interview Tips
Time: Spend 10 minutes explaining each principle with simple examples, 10 minutes discussing how they work together, and 5 minutes answering questions.
Explain each principle clearly and simply.
Show how principles improve code quality and maintainability.
Use relatable examples to illustrate concepts.
Discuss trade-offs and real-world application.
Highlight how principles reduce bugs and ease scaling.
Practice
(1/5)
1. Which of the following best describes the Single Responsibility Principle (SRP) in object-oriented design?
easy
A. Classes should be tightly coupled to improve performance.
B. A class should inherit from multiple classes to reuse code.
C. Objects should be able to change their behavior at runtime.
D. A class should have only one reason to change, meaning it should have only one job.
Solution
Step 1: Understand SRP definition
The Single Responsibility Principle states that a class should have only one reason to change, meaning it should focus on a single job or responsibility.
Step 2: Evaluate options against SRP
A class should have only one reason to change, meaning it should have only one job. matches this definition exactly. Options A, B, and C describe other concepts or incorrect ideas.
Final Answer:
A class should have only one reason to change, meaning it should have only one job. -> Option D
Quick Check:
Single Responsibility Principle = One class, one job [OK]
Hint: SRP means one class, one responsibility [OK]
Common Mistakes:
Confusing SRP with inheritance rules
Thinking SRP allows multiple unrelated jobs in one class
Believing tight coupling is good design
2. Which syntax correctly demonstrates the Open/Closed Principle in class design?
easy
A. class Shape { void draw() {} } class Circle { void draw() { /* draw circle */ } }
B. class Shape { void draw() {} } class Circle extends Shape { void draw() { /* draw circle */ } }
C. class Shape { void draw() {} } class Circle extends Shape { void draw() { super.draw(); } }
D. class Shape { void draw() {} } class Circle extends Shape { void draw() { /* draw circle */ } } class Square extends Circle {}
Solution
Step 1: Understand Open/Closed Principle (OCP)
OCP means software entities like classes should be open for extension but closed for modification. This means you can add new behavior by extending classes without changing existing code.
Step 2: Analyze options for OCP compliance
class Shape { void draw() {} } class Circle extends Shape { void draw() { /* draw circle */ } } shows a base class Shape and a subclass Circle that extends Shape and overrides draw(), adding new behavior without modifying Shape. This follows OCP. class Shape { void draw() {} } class Circle { void draw() { /* draw circle */ } } lacks inheritance, so no extension. class Shape { void draw() {} } class Circle extends Shape { void draw() { super.draw(); } } calls super.draw() but does not add new behavior. class Shape { void draw() {} } class Circle extends Shape { void draw() { /* draw circle */ } } class Square extends Circle {} extends Circle with Square, which is allowed but less clear and may break OCP if Square changes Circle behavior.
Final Answer:
class Shape { void draw() {} } class Circle extends Shape { void draw() { /* draw circle */ } } -> Option B
Quick Check:
Open/Closed = Extend by subclassing, no base change [OK]
Hint: OCP means extend classes, don't modify them [OK]
Common Mistakes:
Ignoring inheritance for extension
Modifying base class instead of extending
Confusing method overriding with no added behavior
3. Consider the following Python code snippet applying the Liskov Substitution Principle (LSP):
Bird class has fly() returning "Flying". Penguin inherits Bird but overrides fly() to return "Cannot fly". bird.fly() prints "Flying". penguin.fly() prints "Cannot fly".
Step 2: Predict output lines
First print outputs "Flying". Second print outputs "Cannot fly" as per Penguin's override.
DIP states that high-level modules should not depend on low-level modules directly; both should depend on abstractions (interfaces or abstract classes).
Step 2: Analyze code for DIP violation
Switch class creates and uses a concrete LightBulb instance directly, tightly coupling Switch to LightBulb. This violates DIP because Switch depends on a concrete class, not an abstraction.
Final Answer:
Switch class directly depends on concrete LightBulb class instead of an abstraction. -> Option C
Quick Check:
DIP = Depend on abstractions, not concrete classes [OK]
Hint: High-level classes should depend on interfaces, not concrete classes [OK]
Common Mistakes:
Thinking methods in LightBulb are wrong
Believing multiple instances are needed
Confusing inheritance with dependency
5. You are designing a payment system that must support multiple payment methods (credit card, PayPal, crypto). Which object-oriented design principle helps you add new payment methods without changing existing code, and how should you apply it?
hard
A. Use the Open/Closed Principle by defining a Payment interface and implementing new payment classes for each method.
B. Use the Single Responsibility Principle by putting all payment logic in one PaymentProcessor class.
C. Use the Liskov Substitution Principle by making all payment classes inherit from a base Payment class but override methods differently.
D. Use the Dependency Inversion Principle by making payment classes depend on concrete implementations.
Solution
Step 1: Identify the need for extensibility
The system must allow adding new payment methods without modifying existing code, which is a classic use case for the Open/Closed Principle (OCP).
Step 2: Apply OCP with interface and implementations
Define a Payment interface with common methods. Implement each payment method (credit card, PayPal, crypto) as separate classes implementing this interface. This allows adding new methods by creating new classes without changing existing ones.
Final Answer:
Use the Open/Closed Principle by defining a Payment interface and implementing new payment classes for each method. -> Option A
Quick Check:
OCP = Add new classes, no existing code change [OK]
Hint: OCP means add new classes, don't modify old ones [OK]
Common Mistakes:
Putting all logic in one class violating SRP
Confusing inheritance with extensibility
Depending on concrete classes instead of abstractions
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.