How to use: Click Hide answers to enter self-test mode — each question collapses, and you tap it to check yourself after answering aloud. Toggle back to read straight through, or Print for a paper copy. Anything you can’t reconstruct from memory is a weak spot to revisit.
It converts a high-level design into detailed, implementable components — class structures, object relationships, APIs, and internal logic — before coding, removing ambiguity and keeping the system scalable and maintainable.
Car, ParkingSpot, and ParkingLot classes and their relationships.It turns architecture into implementation-ready modules, giving a clear structure, reducing errors, and improving code quality by defining object interactions and APIs before coding.
Driver, Rider, RideRequest, and Payment interact.Splitting a system into small, independent, single-responsibility modules makes it easier to debug, test, and extend, and lets teams work in parallel.
Access patterns, time-complexity needs, memory constraints, concurrency, and scalability.
Arrays, linked lists, stacks, queues, hash maps, trees, graphs, and heaps — chosen by lookup speed, insertion/deletion, ordering, and scalability.
Data size, distribution, memory availability, stability, parallelizability, and whether the work is in-memory or on disk.
Encapsulation (bundle data, hide internals), Abstraction (expose only essentials), Inheritance (reuse via is-a), and Polymorphism (one interface, many behaviors) — improving reusability, flexibility, security, and maintainability.
Modeling a system as objects and classes and their interactions, producing modular, reusable, scalable, and maintainable software.
Account, Customer, and Transaction classes.Abstraction hides complexity, exposing only essential functionality through interfaces/abstract classes. Encapsulation hides data, restricting access via private fields with getters/setters.
Inheritance is “is-a” (a subclass acquires a parent’s behavior); composition is “has-a” (built from other objects). Prefer composition — more flexible and loosely coupled than deep inheritance trees.
Car has-an Engine (composition); a Dog is-an Animal (inheritance).The same method or interface behaving differently by context — compile-time (overloading) or runtime (overriding) — enabling generic, reusable code.
payment() method behaves differently for Credit Card, UPI, and PayPal.An interface is a pure contract (methods only, multiple inheritance); an abstract class can hold partial implementation and shared state. Use an interface for a behavior contract, an abstract class when subclasses share state/logic.
Vehicle interface defines start()/stop(); an abstract Vehicle class adds shared speed and fuelType.Relationships differing in ownership strength: association is a loose “uses,” aggregation is weak ownership (child lives independently), composition is strong ownership (child dies with the parent).
They define a contract so components communicate via abstraction rather than concrete implementations, enabling multiple implementations, dependency inversion, and easy mocking in tests.
Notification interface with Email, SMS, and Push implementations swaps with no client changes.SRP — one reason to change; OCP — open for extension, closed for modification; LSP — subtypes substitutable for their base type; ISP — no fat interfaces clients don’t use; DIP — depend on abstractions, not concretions. Together they yield clean, maintainable, scalable code.
Don’t Repeat Yourself — avoid duplicated logic and keep a single source of truth.
ValidationService instead of repeating validation across classes.Keep It Simple — build the simplest thing that works and avoid over-engineering.
You Aren’t Gonna Need It — build only what’s currently required.
Classes overly dependent on each other, hard to change, reuse, or test; prefer loose coupling via interfaces, abstraction, and dependency injection.
PaymentService directly instantiates a PayPal object, swapping providers means editing the service.Providing a class’s dependencies from outside instead of creating them internally, giving loose coupling and testability (inject mocks).
Database into UserService via the constructor rather than instantiating it inside.Signs of poor design — long methods, duplicated code, God classes, tight coupling, deep conditionals — fixed by refactoring: modularizing, applying patterns, better naming, and following SOLID.
Reusable, proven solutions to common object-oriented design problems, giving a shared vocabulary and improving maintainability, scalability, and flexibility.
Creational (Singleton, Factory, Abstract Factory), Structural (Adapter, Facade, Proxy, Decorator), and Behavioral (Observer, Strategy, Command, Chain of Responsibility).
When they add needless complexity to a problem a simple approach solves; use a pattern only for a real, recurring design problem.
The nature and complexity of the problem, how often it’s expected to change, and the impact on flexibility, performance, and maintainability.
Yes — they address different concerns and complement each other.
They structure interactions through abstractions and indirection, reducing tight coupling and localizing changes.
Ensures a single instance with a global access point — used for DB connection pools, config managers, and logging. Downsides: global state (harder testing), tight coupling, reduced flexibility.
Global state that’s hard to test and maintain, tight coupling, reduced flexibility, and difficulty mocking in unit tests — which is why DI is often better.
A subject maintains a list of observers and automatically notifies them on a state change — a one-to-many relationship without tight coupling.
Creates objects without exposing creation logic; a factory method decides the concrete class, centralizing creation and promoting loose coupling — used when the type isn’t known until runtime.
PaymentFactory returns UPI, Card, or Wallet objects based on input.Use Abstract Factory to create families of related objects that must be compatible together; Factory Method handles one product at a time.
Static factory methods are simple methods returning objects, tied to one class with no polymorphism; the Factory pattern is structured and extensible via interfaces and subclasses.
User.createUser() returns a User directly, whereas a Factory creates different User types without client changes.Encapsulates interchangeable algorithms and lets you swap them at runtime, avoiding hardcoded logic.
A Proxy controls access (security, lazy loading, remote); a Decorator adds behavior dynamically.
Passes a request along a chain of handlers until one handles it, decoupling sender from receiver.
Encapsulates a request as an object storing what’s needed to execute and reverse it, kept in a history stack to enable undo/redo.
Replaces null with a do-nothing object implementing the same interface, eliminating repetitive null checks.
NullLogger whose log() does nothing, instead of checking if (logger != null) everywhere.Class, Sequence, Use Case, Activity, and State diagrams — visualizing structure, flow, interactions, and behavior.
Account/Customer, and a Sequence Diagram shows a money-transfer flow.Diagrams showing dynamic behavior — how objects interact and how the system changes over time in response to events — versus structural diagrams.
Time-ordered messages between components: User → Login Controller → Authentication Service → Database → back with success or failure, capturing the step-by-step message passing.
Show an object’s states and the event-triggered transitions between them — its lifecycle.
It creates an extra structure (B-tree, hash, or bitmap) so the database locates rows without a full scan, giving faster SELECTs and less disk I/O at the cost of storage and slightly slower writes.
user_id jumps straight to a record.Organizing tables to cut redundancy and improve integrity, making inserts, updates, and deletes reliable.
Customers and Orders tables linked by a customer id, instead of repeating customer details on every order.Normalization, proper relationships, indexing, constraints, and suitable data types, along with scalability, security, and efficient storage.
It lets multiple threads safely access shared resources without inconsistency, preventing race conditions, deadlocks, and corrupted state.
Use API versioning and controlled database migrations, keeping old versions working through gradual deprecation, feature flags, and regression testing.
/api/v1/users alive while /api/v2/users adds new fields.Combine secure identity — OAuth 2.0, JWT, MFA, password hashing — with authorization (RBAC/ABAC) and central identity providers that validate tokens per service.
Use structured logging with log levels and centralized aggregation, plus dashboards and alerting on latency, CPU, and errors, and attach a correlation id per request to trace it end to end.
Model the core classes and responsibilities: ParkingLot (holds levels), Level, ParkingSpot (compact/large/handicapped), Vehicle (Car/Bike/Truck via inheritance), Ticket, and PaymentProcessor. Apply OOP (inheritance for vehicle/spot types), Strategy (pricing), and Factory (spot/vehicle creation), and handle concurrency for spot allocation. It’s the canonical “show me your OOP + patterns” question.