Relationships π
In Object-Oriented Programming (OOP), relationships define how classes and objects interact with each other. There are several fundamental types of relationships in OOP:
Inheritance (IS-A Relationship)β
Inheritance represents an is-a relationship. A subclass inherits properties and behaviors (methods) from a parent class.
class Vehicle {
void start() {
System.out.println("Vehicle started");
}
}
class Car extends Vehicle {
void honk() {
System.out.println("Car honks");
}
}
Caris aVehicle, so it inherits thestart()method.- You can call
Car().start()because of inheritance. - This promotes code reuse.
Use it when:
- There is a true hierarchy.
- The child class should inherit behavior from the parent.
- The subclass can replace the parent without breaking logic (Liskov Substitution Principle).
- Share common field, constructor.
- Child class is type of parent.
- Answer the question
Who?
Composition (HAS-A Relationship)β
Composition represents a has-a relationship. One class contains another class as a part.
class Engine {
void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
Car() { // constructor
engine = new Engine(); // Car has an Engine
}
void start() {
engine.start();
System.out.println("Car started");
}
}
Carhas anEngine.- Engine exists as part of Car, showing strong ownership.
- When Car is destroyed, Engine is usually destroyed too.
Use it when:
- One object cannot exist meaningfully without the other.
- The contained object is created and destroyed with the parent.
- There is strong ownership.
- Answer the question
what,where
Aggregation (HAS-A Relationship, weaker than composition)β
Aggregation is also a has-a relationship but weaker. The contained object can exist independently of the parent.
class Department {
String name;
Department(String name) {
this.name = name;
}
}
class University {
ArrayList<Department> departments;
University() {
departments = new ArrayList<>(); // University has departments
}
void addDepartment(Department dept) {
departments.add(dept);
}
}
UniversityhasDepartments.- Departments can exist even if the University object is destroyed.
- This is loose coupling.
Use it when:
- The child objects can exist independently.
- Multiple parents may share the same object.
Association (General Relationship)β
Association is a general connection between classes. Neither class owns the other necessarily.
class Teacher {
String name;
Teacher(String name) {
this.name = name;
}
}
class Student {
String name;
Student(String name) {
this.name = name;
}
void assignTeacher(Teacher teacher) {
System.out.println(this.name + " is assigned to " + teacher.name);
}
}
StudentandTeacherare related via assignment.- Both can exist independently.
- Association is the most general relationship.
Use it when:
- Classes communicate but do not own each other.
- Relationship is loose.
Dependency (Uses-A Relationship)β
Dependency occurs when a class uses another class temporarily.
class Printer {
void printDocument(String doc) {
System.out.println("Printing: " + doc);
}
}
class Computer {
private Printer printer;
Computer() {
printer = new Printer(); // Computer has a Printer
}
void doPrint(String document) {
printer.printDocument(document);
}
}
Computerdepends onPrinterto print documents.- If Printer changes, Computer may need updates.
- Dependency is temporary or short lived.
Use it when:
- A class needs another class only inside a method.
- The dependency is short-lived.
Can-Do Relationship (Ability / Interface)β
A Can-Do relationship represents what an object can do, usually expressed via interfaces, abstract classes, or protocols. Itβs about capabilities, not ownership or hierarchy.
-
Itβs often phrased as: βX can do Yβ, e.g., a
Birdcan fly, aCarcan honk. -
The class implements behavior, but it may not inherit from a parent class.
Example(using abstract base classes):
interface Drivable {
void drive();
}
class Car implements Drivable {
@Override
public void drive() {
System.out.println("Car is driving");
}
}
class Truck implements Drivable {
@Override
public void drive() {
System.out.println("Truck is driving");
}
}
Carcan drive,Truckcan drive β both implement theDrivableinterface.- Thereβs no inheritance hierarchy between Car and Truckβjust a shared capability.
- This allows polymorphism: any
Drivableobject can be asked todrive()without knowing its concrete type.
static void makeItDrive(Drivable vehicle) {
vehicle.drive();
}
Car car = new Car();
Truck truck = new Truck();
makeItDrive(car);
makeItDrive(truck);
- Here,
makeItDrive()works with any object that can fly, demonstrating flexible, decoupled design.
Use it when:
- Many unrelated classes share a capability.
- You want polymorphism.
- Behavior should be plug-and-play.
Key Points about Can-Do:
| Aspect | Explanation |
|---|---|
| Relation type | Capability / interface |
| Expressed as | Interface, abstract class, protocol |
| Example | Bird can fly, Airplane can fly |
| Advantage | Enables polymorphism and flexible design |
| Lifetime | Independent; objects may or may not implement it |
In short:
IS-Aβ βCar is a VehicleβHAS-Aβ βCar has EngineβCan-Doβ βBird can flyβ
Quick Summary Tableβ
| Relationship | Type | Example | Lifetime of object | Notes |
|---|---|---|---|---|
| Inheritance | IS-A | Car => Vehicle | Same as subclass | Code reuse, polymorphism |
| Composition | HAS-A | Car => Engine | Part destroyed with whole | Strong ownership |
| Aggregation | HAS-A | University => Dept | Independent | Weak ownership |
| Association | General | Student <=> Teacher | Independent | General relationship |
| Dependency | Uses-A | Computer uses Printer | Temporary | Short-term usage |
| Can-Do | Capability | Car => Drivable | Independent | Behavior via interface |