Types of Class Relationships
Choose relationships intentionally to balance reuse, coupling, and maintainability.
IS-A (inheritance), HAS-A (composition/aggregation), USES-A (dependency), association, realization (interface implementation), and dependency injection for loose coupling.Definition
Relationship choice directly impacts extensibility, testability, and coupling. In interviews, you should justify why a relation is strong (IS-A) or weak (USES-A) based on domain behavior.
When to Use
- Use
IS-Afor valid polymorphic substitution. - Use composition/aggregation to model ownership and collaboration.
- Use interfaces + DI for decoupled, testable service design.
When NOT to Use
- Do not force inheritance for code reuse alone.
- Avoid deep inheritance trees that become brittle.
- Avoid hard-coded concrete dependencies inside business services.
Quick Summary
| Aspect | Type | Strength | Typical Example |
|---|---|---|---|
| Inheritance | Strong | Dog -> Animal | class Dog extends Animal |
| Composition | Very strong | Car -> Engine | private Engine engine = new Engine(); |
| Aggregation | Medium | Company -> Employee | List<Employee> employees; |
Dependency (USES-A) | Weak/temporary | PaymentService uses Order | void pay(Order order) |
| Association | Varies | School with Student/Teacher | List<Student>, List<Teacher> |
| Realization | Strong contract | UpiPayment implements Payment | class UpiPayment implements Payment |
| Dependency Injection | Architectural (loosely coupled) | OrderService(Payment payment) | this.payment = payment; |
Common Confusions Cleared Up
Prefer composition when behavior may vary at runtime or hierarchy is unstable. Use inheritance only when true substitutability (IS-A) holds.
Composition implies owner controls lifecycle; aggregation implies referenced object can live independently and be shared.
Dependency is usually short-lived usage (method-level). Association is structural relationship retained as field/reference.
Variations / Implementations
1) IS-A (Inheritance)
Represents specialization: child extends parent behavior and is substitutable for parent in polymorphic code.
class Animal {
void eat() {}
}
class Dog extends Animal {
void bark() {}
}Dog IS-A Animal.2) HAS-A (Composition / Aggregation)
Represents ownership or containment. Composition is lifecycle-dependent; aggregation is lifecycle-independent.
class Engine {}
class Car { // composition (strong HAS-A)
private final Engine engine = new Engine();
}
class Employee {}
class Company { // aggregation (weak HAS-A)
private List<Employee> employees;
}3) USES-A (Dependency)
Temporary relationship where one class depends on another for a method call/task (parameter/local variable).
class PaymentService {
void pay(Order order) {
// uses Order temporarily
}
}4) Association
General long-lived relationship between entities. Can be one-to-one, one-to-many, or many-to-many.
class Student {}
class Teacher {}
class School {
List<Student> students;
List<Teacher> teachers;
}5) Realization (Interface Implementation)
A class realizes/implements behavior declared by an interface. Core to contract-first design and polymorphism.
interface Payment {
void pay();
}
class UpiPayment implements Payment {
public void pay() {}
}6) Dependency Injection (Design pattern over dependency)
Inject dependencies from outside instead of creating them internally. Improves testability and loose coupling; heavily used in Spring.
class OrderService {
private final Payment payment;
OrderService(Payment payment) {
this.payment = payment;
}
}Common Mistakes / Anti-Patterns
- Inheritance used where relationship is actually
HAS-A. - God class associated with too many unrelated collaborators.
- Service creating dependencies with
neweverywhere (hard to test). - Interface explosion without meaningful abstractions.