Title: Oop Principles — LearnCoachAssist

Q: What is encapsulation in OOP?
A: Encapsulation is the bundling of data and methods that operate on that data into a single unit (class), while restricting direct access to some components.
Key benefits:
- Hides internal state and implementation details
- Exposes only a controlled public interface
- Protects object integrity by preventing unintended modification

Q: What is inheritance in OOP?
A: Inheritance allows a child class (subclass) to acquire the properties and behaviors of a parent class (superclass).
Example in Java:
class Dog extends Animal { }
Benefits:
- Promotes code reuse
- Establishes an is-a relationship
- Enables polymorphic behavior

Q: What is polymorphism in OOP?
A: Polymorphism means "many forms" — the ability of an object to take on different behaviors depending on its type.
Two main types:
- Compile-time (static): method overloading
- Run-time (dynamic): method overriding via inheritance
Example: a Shape.draw() method behaves differently for Circle vs Rectangle.

Q: What is abstraction in OOP?
A: Abstraction is the process of hiding complex implementation details and exposing only the essential features of an object.
Achieved through:
- Abstract classes — partial implementation
- Interfaces — pure contract, no implementation
Example: a Vehicle interface exposes start() and stop() without revealing engine internals.

Q: What is the Single Responsibility Principle (SRP)?
A: The S in SOLID. A class should have only one reason to change, meaning it should have only one job or responsibility.
Violation example: a UserService class that handles authentication, email sending, and database queries.
Fix: split into AuthService, EmailService, and UserRepository.

Q: What is the Open/Closed Principle (OCP)?
A: The O in SOLID. Software entities should be open for extension but closed for modification.
In practice:
- Add new behavior by creating new classes or methods
- Don't modify existing, tested code
- Use interfaces, abstract classes, and strategy patterns to enable extension

Q: What is the Liskov Substitution Principle (LSP)?
A: The L in SOLID. Objects of a superclass should be replaceable with objects of a subclass without breaking the program.
Classic violation: Square extends Rectangle — setting width on a Square unexpectedly changes height.
Rule: subclasses must honor the behavioral contract of the parent class.

Q: What is the Interface Segregation Principle (ISP)?
A: The I in SOLID. Clients should not be forced to depend on interfaces they do not use.
Violation: a fat Worker interface with work(), eat(), sleep() — a Robot doesn't eat or sleep.
Fix: split into Workable, Eatable, and Sleepable interfaces.

Q: What is the Dependency Inversion Principle (DIP)?
A: The D in SOLID. High-level modules should not depend on low-level modules — both should depend on abstractions.
Example:
class OrderService { private PaymentGateway gateway; }
Here PaymentGateway is an interface, not a concrete class like StripePayment. This enables easy swapping of implementations.

Q: What is composition vs inheritance?
A: Composition models a has-a relationship; inheritance models an is-a relationship.
Composition example: Car has an Engine.
Inheritance example: Dog is an Animal.
The principle "favor composition over inheritance" encourages using composition for code reuse because it provides greater flexibility and avoids tight coupling.

Q: Why is composition often preferred over inheritance?
A: Composition is preferred because:
- Loose coupling — components can be swapped at runtime
- No fragile base class problem — changes to a parent don't break children
- Greater flexibility — combine behaviors from multiple sources
- Avoids deep hierarchies that are hard to understand
Use inheritance only when a true is-a relationship exists.

Q: What is the difference between an interface and an abstract class?
A: Interface:
- Defines a contract with no state (traditionally)
- A class can implement multiple interfaces
- All methods are implicitly public abstract (in Java)
Abstract class:
- Can have state (fields) and concrete methods
- A class can extend only one abstract class
- Can define constructors and access modifiers

Q: What is coupling in software design?
A: Coupling measures the degree of interdependence between modules.
- Tight coupling: classes depend heavily on each other's internals — hard to change or test
- Loose coupling: classes interact through abstractions — easy to modify and test independently
Goal: minimize coupling using interfaces, dependency injection, and encapsulation.

Q: What is cohesion in software design?
A: Cohesion measures how closely related the responsibilities within a single module are.
- High cohesion: a class does one well-defined job (e.g., EmailSender)
- Low cohesion: a class handles unrelated tasks (e.g., a utility class doing everything)
Goal: maximize cohesion — each class should have a focused, single purpose.

Q: What is the DRY principle?
A: DRY stands for "Don't Repeat Yourself". Every piece of knowledge should have a single, authoritative representation in the system.
Violations include:
- Copy-pasted logic across classes
- Duplicated validation rules
- Repeated SQL queries
Fix: extract shared logic into reusable methods, base classes, or utility functions.

Q: What is the KISS principle?
A: KISS stands for "Keep It Simple, Stupid". Systems work best when they are kept simple rather than made complex.
Guidelines:
- Avoid over-engineering and premature abstraction
- Write straightforward, readable code
- Prefer clear solutions over clever ones
- Reduce unnecessary layers of indirection

Q: What is the YAGNI principle?
A: YAGNI stands for "You Aren't Gonna Need It". Don't implement functionality until it is actually needed.
Common violations:
- Adding unused configuration options "just in case"
- Creating abstract factories for a single implementation
- Building features for hypothetical future requirements
Focus on current requirements and refactor when needs arise.

Q: What is method overloading?
A: Method overloading (compile-time polymorphism) means defining multiple methods with the same name but different parameter lists in the same class.
Example in Java:
int add(int a, int b) { ... }
double add(double a, double b) { ... }
The compiler selects the correct method based on the argument types at compile time.

Q: What is method overriding?
A: Method overriding (runtime polymorphism) means a subclass provides a specific implementation of a method already defined in its superclass.
Example:
class Animal { void speak() { } }
class Dog extends Animal { void speak() { print("Woof"); } }
The method called depends on the actual object type at runtime, not the reference type.

Q: What is the difference between overloading and overriding?
A: | Overloading | Overriding
Binding | Compile-time | Runtime
Scope | Same class | Parent-child classes
Signature | Different parameters | Same signature
Return type | Can differ | Must be same or covariant
Overloading = static polymorphism; Overriding = dynamic polymorphism.

Q: What are access modifiers in OOP?
A: Access modifiers control the visibility of class members:
- public — accessible from anywhere
- private — accessible only within the declaring class
- protected — accessible within the class and its subclasses
- package-private (default in Java) — accessible within the same package only
They enforce encapsulation by restricting access.

Q: When should you use private vs protected access?
A: Use private when:
- The member is an internal implementation detail
- No subclass needs direct access
Use protected when:
- Subclasses need to access or override the member
- You want to allow extension while hiding from external code
Default to private and only widen access when there is a clear need.

Q: What is a constructor in OOP?
A: A constructor is a special method called automatically when an object is created. It initializes the object's state.
Key properties:
- Has the same name as the class (in Java/C++)
- Has no return type
- Can be overloaded with different parameter lists
- A default constructor is provided if none is defined
Example: new Car("Toyota", 2024)

Q: What is a destructor and when is it used?
A: A destructor is a method called when an object is destroyed or goes out of scope. It releases resources like file handles, memory, or network connections.
In C++: ~ClassName() { } — called deterministically.
In Python: __del__(self) — called by garbage collector.
In Java: no true destructor; use try-with-resources or AutoCloseable instead.

Q: What is the difference between static and instance members?
A: Static members belong to the class itself:
- Shared across all instances
- Accessed via class name: Math.PI
- Cannot access instance variables
Instance members belong to each object:
- Unique per instance
- Accessed via object reference: dog.name
- Can access both static and instance members

Q: When should you use static methods?
A: Use static methods when:
- The method doesn't depend on instance state (e.g., utility/helper functions)
- It performs a pure computation: Math.max(a, b)
- It serves as a factory method: List.of(1, 2, 3)
Avoid static methods when behavior needs to be polymorphic or when it creates tight coupling to a specific implementation.

Q: What is the difference between a class and an object?
A: A class is a blueprint or template that defines attributes and behaviors.
An object is a concrete instance of a class created at runtime.
Analogy: a class is like an architectural plan; an object is the actual building.
Example:
class Car { String color; }
Car myCar = new Car(); // object
Multiple objects can be created from a single class.

Q: What is association in OOP?
A: Association is a general relationship where one object uses or interacts with another. Neither object owns the other.
Example: a Teacher and a Student — both exist independently.
Types:
- Unidirectional: Teacher knows Student, but not vice versa
- Bidirectional: both know each other
It is the weakest form of object relationship.

Q: What is aggregation in OOP?
A: Aggregation is a specialized association representing a whole-part relationship where the part can exist independently of the whole.
Example: a Department has Professor objects — if the department is dissolved, professors still exist.
In UML, it's shown as a hollow diamond on the whole side.
It represents a "has-a" relationship with weak ownership.

Q: What is composition (relationship) in OOP?
A: Composition is a strong form of aggregation where the part cannot exist without the whole. If the whole is destroyed, the parts are too.
Example: a House has Room objects — rooms don't exist without the house.
In UML, shown as a filled diamond.
It represents strong ownership and a strict lifecycle dependency between objects.

Q: How do association, aggregation, and composition differ?
A: All describe object relationships, but differ in ownership strength:
- Association: objects are related but independent — Teacher ↔ Student
- Aggregation: whole contains parts, but parts survive independently — Library → Book
- Composition: whole owns parts with shared lifecycle — House → Room
From weakest to strongest coupling: Association → Aggregation → Composition.

Q: What is Design by Contract (DbC)?
A: Design by Contract, introduced by Bertrand Meyer, defines formal agreements between a method and its callers:
- Preconditions: what must be true before calling the method
- Postconditions: what the method guarantees after execution
- Invariants: conditions that must always hold for the class
Example: a withdraw(amount) method requires amount > 0 (precondition) and ensures balance >= 0 (postcondition).

Q: What is the Law of Demeter (LoD)?
A: The Law of Demeter (Principle of Least Knowledge) states: a method should only talk to its immediate friends, not strangers.
Violation: order.getCustomer().getAddress().getCity() — method chain reaches deep into unrelated objects.
Fix: order.getShippingCity() — delegate responsibility.
Benefits: reduced coupling, easier maintenance, and better encapsulation.

Q: What are mixins in OOP?
A: Mixins are classes that provide reusable methods to other classes without being a standalone base class. They add behavior through multiple inheritance or similar mechanisms.
Python example:
class JsonMixin:
  def to_json(self): ...
class User(Model, JsonMixin): ...
Mixins promote code reuse without deep inheritance hierarchies.

Q: What are traits in OOP?
A: Traits are reusable sets of methods that can be composed into classes. Similar to mixins but with explicit conflict resolution.
Used in: Scala, PHP, Rust (as trait keyword).
PHP example:
trait Timestampable {
  public function getCreatedAt() { ... }
}
class Post { use Timestampable; }
Traits solve the code reuse problem without multiple inheritance.

Q: What is multiple inheritance?
A: Multiple inheritance allows a class to inherit from more than one parent class.
Supported in: C++, Python.
Not supported in: Java, C# (use interfaces instead).
Example in Python:
class FlyingFish(Fish, Bird): ...
Risks:
- Diamond problem — ambiguity in method resolution
- Increased complexity

Q: What is the diamond problem?
A: The diamond problem occurs in multiple inheritance when a class inherits from two classes that share a common ancestor, creating ambiguity.
A
↙ ↘
B   C
↘ ↙
D
If B and C both override a method from A, which version does D get?
Solutions:
- Python: MRO (C3 linearization)
- C++: virtual inheritance
- Java: disallows multiple class inheritance

Q: What is an abstract class?
A: An abstract class is a class that cannot be instantiated and may contain both abstract (unimplemented) and concrete methods.
Example in Java:
abstract class Shape {
  abstract double area();
  void describe() { System.out.println("I am a shape"); }
}
Use when subclasses share common code but must implement specific behaviors.

Q: What is an interface in OOP?
A: An interface defines a contract of methods that implementing classes must provide. It specifies what a class does, not how.
Java example:
interface Drawable {
  void draw();
}
class Circle implements Drawable {
  void draw() { ... }
}
Modern interfaces (Java 8+) can include default and static methods.

Q: What is the difference between tight and loose coupling?
A: Tight coupling: classes depend on concrete implementations. Changing one class forces changes in others.
OrderService service = new OrderService(new MySQLDatabase());
Loose coupling: classes depend on abstractions. Easy to swap implementations.
OrderService service = new OrderService(database); // database is an interface
Loose coupling enables testability, flexibility, and maintainability.

Q: What is the fragile base class problem?
A: The fragile base class problem occurs when changes to a base class unintentionally break its subclasses.
Example: adding a method to a parent class that conflicts with a subclass method, or changing internal logic that subclasses depend on.
Mitigations:
- Favor composition over inheritance
- Mark classes as final when not designed for extension
- Document which methods are safe to override

Q: What is dependency injection?
A: Dependency injection (DI) is a technique where an object's dependencies are provided externally rather than created internally.
Three types:
- Constructor injection: new Service(repository)
- Setter injection: service.setRepo(repository)
- Interface injection: implement an injector interface
DI supports the Dependency Inversion Principle and makes code testable and flexible.

Q: What is the difference between composition and aggregation?
A: Both are whole-part relationships, but differ in lifecycle dependency:
Aggregation (weak):
- Part exists independently: Team → Player
- UML: hollow diamond ◇
Composition (strong):
- Part cannot exist without whole: Invoice → LineItem
- UML: filled diamond ◆
Composition implies ownership; aggregation implies usage.

Q: What is a virtual method?
A: A virtual method is a method declared in a base class that can be overridden by subclasses, with the call resolved at runtime (dynamic dispatch).
In C++: virtual void draw();
In Java: all non-static, non-final methods are virtual by default.
In C#: explicitly marked with virtual keyword.
Virtual methods enable runtime polymorphism and are key to the Template Method pattern.

Q: What is a pure virtual function?
A: A pure virtual function (C++) is a virtual function with no implementation in the base class, forcing subclasses to provide one.
Syntax: virtual void draw() = 0;
A class with at least one pure virtual function becomes an abstract class and cannot be instantiated.
Equivalent in Java: an abstract method.
Equivalent in Python: using @abstractmethod from the abc module.

Q: What is the difference between early and late binding?
A: Early binding (static binding): method call is resolved at compile time.
- Used for: overloaded methods, static methods, final methods
- Faster but less flexible
Late binding (dynamic binding): method call is resolved at runtime.
- Used for: overridden (virtual) methods
- Enables polymorphism
Late binding checks the actual object type, not the declared type.

Q: What is the purpose of the final keyword in OOP?
A: The final keyword prevents modification:
- Final class: cannot be subclassed — final class String { }
- Final method: cannot be overridden by subclasses
- Final variable: value cannot be changed after initialization (constant)
Use final to enforce immutability, prevent fragile base class issues, and communicate design intent.

Q: What is the Principle of Least Knowledge?
A: The Principle of Least Knowledge (also called the Law of Demeter) states that a unit should have limited knowledge of other units and only interact with closely related ones.
A method m of object O should only call methods on:
- O itself
- m's parameters
- Objects created within m
- O's direct fields
This minimizes coupling between components.

Q: What is object identity vs equality?
A: Identity: two references point to the same object in memory.
Java: a == b
Python: a is b
Equality: two objects have the same value/state.
Java: a.equals(b)
Python: a == b
Important: always override equals() and hashCode() together in Java. Two equal objects must have the same hash code, but two objects with the same hash code are not necessarily equal.

Q: What is the SOLID acronym in full?
A: SOLID stands for five OOP design principles:
- S — Single Responsibility Principle
- O — Open/Closed Principle
- L — Liskov Substitution Principle
- I — Interface Segregation Principle
- D — Dependency Inversion Principle
Together they promote maintainable, flexible, and testable object-oriented software. Introduced by Robert C. Martin (Uncle Bob).