
Interviews: Object Oriented Design
Intro
Refresher on basic ood fundamentals, design patterns, concurrency, etc.
based on: https://github.com/ashishps1/awesome-low-level-design
OOP Fundamentals
Classes and Objects
Enums
Interfaces
Encapsulation
Abstraction
Inheritance
Polymorphism
Class Relationships
Association
Aggregation
Composition
Dependency
Design Principles
SOLID
5 principles intended to make source code more understandable, flexible, and maintainable
S: Single Responsibility
A class should have a single responsibility
O: Open-Closed
Classes should be open for extension, but closed for modification.
L: Liskov Substitution
If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S, without altering any of the desirable properties of that program.
I: Interface Segregation
Clients should not be forced to depend on methods that they do not use.
D: Dependency Inversion
High level modules should not depend on low level modules. Both should depend on the abstraction.
Abstractions should not depend on details. Details should depend on abstractions
YAGNI (You Aren't Gonna Need It)
Don't add features or functionality to our software that we currently don't need.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
# Future-proofing for possible new features
address: str | None = None
phone_number: str | None = None
def create_user(name: str, age: int) -> User:
# Simplified for current needs
return User(name=name, age=age)instead of
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
def create_user(name: str, age: int) -> User:
return User(name=name, age=age)KISS (Keep It Simple Stupid)
Don't add unnecessary complexity to our software
def get_even_numbers(numbers: list[int]) -> list[int]:
even_numbers: list[int] = []
for i in range(len(numbers)):
if numbers[i] % 2 == 0:
even_numbers.append(numbers[i])
return even_numbersdef get_even_numbers(numbers: list[int]) -> list[int]:
return [number for number in numbers if number % 2 == 0]DRY (Don't Repeat Yourself)
Don't duplicate code or data in our software
from dataclasses import dataclass
@dataclass
class Product:
id: int
name: str
price: float
def sort_by_name(products: list[Product]) -> list[Product]:
return sorted(products, key=lambda p: p.name)
def sort_by_price(products: list[Product]) -> list[Product]:
return sorted(products, key=lambda p: p.price)
def sort_by_id(products: list[Product]) -> list[Product]:
return sorted(products, key=lambda p: p.id)instead of
from dataclasses import dataclass
from operator import attrgetter
from typing import Literal
ProductField = Literal["id", "name", "price"]
@dataclass
class Product:
id: int
name: str
price: float
def sort_products(products: list[Product], field: ProductField, descending: bool = False) -> list[Product]:
return sorted(products, key=attrgetter(field), reverse=descending)ARY (Always Repeat Yourself)
Find a pattern and stick to it
Consistency across code greatly helps ease of reading. Do not copy & paste the same code all over the place, but do copy & paste class skeletons, reuse variable naming conventions, etc.
Rule Of 3
Don't factor out shared code between two classes, wait until you have at least 3