4 OOP Relation
4.1 Summary of UML Relationships (Weakest to Strongest)
4.1.1 Dependency (Weakest) A - - -> B
// 1. DEPENDENCY (Weakest) - A uses B temporarily
// Notation: A - - - -> B (dashed arrow)
class PaymentProcessor { // Class A
// No stored reference to EmailService
void processPayment(double amount, EmailService emailer) { // Class B as parameter
print('Processing payment of \$$amount');
// Temporary use of EmailService
emailer.sendConfirmation('Payment processed');
// EmailService reference is gone after method ends
}
}- What: A uses B temporarily
- How: B appears in method parameters, return types, or local variables
- Coupling: Very loose - A doesn’t store B
- Example: PaymentProcessor uses EmailService only during payment processing
4.1.2 Association A ——> B
// 2. ASSOCIATION - A has long-term reference to B
// Notation: A ——> B (solid arrow)
class Doctor { // Class A
String name;
List<Patient> patients = []; // Stores references to B
Doctor({required this.name});
void addPatient(Patient patient) {
patients.add(patient);
patient.doctors.add(this); // Bidirectional
}
}
class Patient { // Class B
String name;
List<Doctor> doctors = []; // Can have multiple doctors
Patient({required this.name});
}- What: A has a long-term reference to B
- How: B is stored as a field in A
- Coupling: Moderate - both can exist independently
- Example: Doctor has Patients (and Patients know their Doctors)
4.1.3 Aggregation A ◇——> B
// 3. AGGREGATION - A has B, but B can exist independently
// Notation: A ◇——> B (hollow diamond)
class Department { // Class A
String name;
List<Employee> employees = []; // Has employees but doesn't own them
Department({required this.name});
void addEmployee(Employee emp) {
employees.add(emp);
}
void removeEmployee(Employee emp) {
employees.remove(emp);
// Employee continues to exist
}
}
class Employee { // Class B
String name;
String id;
Employee({required this.name, required this.id});
// Can exist without department
}- What: A “has” B (whole-part relationship)
- How: A contains B but doesn’t control B’s lifecycle
- Coupling: Moderate - B can exist without A
- Example: Department has Employees (but Employees can change departments)
4.1.4 Composition (Strongest “has-a”) A ◆——> B
// 4. COMPOSITION (Strongest "has-a") - A owns B, B cannot exist without A
// Notation: A ◆——> B (filled diamond)
class House { // Class A
String address;
List<Room> rooms = []; // Owns rooms completely
House({required this.address, required int numberOfRooms}) {
// Rooms are created by House
for (int i = 0; i < numberOfRooms; i++) {
rooms.add(Room(houseAddress: address, roomNumber: i + 1));
}
}
void demolish() {
print('House demolished - all rooms destroyed');
rooms.clear(); // Rooms cease to exist
}
}
class Room { // Class B
final String houseAddress; // Cannot change house
final int roomNumber;
Room({required this.houseAddress, required this.roomNumber});
// Cannot exist without House
}- What: A owns B completely
- How: A creates and destroys B
- Coupling: Strong - B cannot exist without A
- Example: House has Rooms (rooms don’t exist without the house)
4.1.5 5. Implementation A - - -▷ B
// 5. IMPLEMENTATION - A implements interface B
// Notation: A - - - ▷ B (dashed arrow with hollow triangle)
abstract class Flyable { // Interface B
void fly();
}
class Bird implements Flyable { // Class A
@override
void fly() {
print('Bird is flying with wings');
}
}
class Airplane implements Flyable { // Another A
@override
void fly() {
print('Airplane is flying with engines');
}
}- What: A implements interface B
- How: A must provide all methods declared in B
- Coupling: Moderate - contract-based relationship
- Example: Bird implements Flyable interface
4.1.6 6. Inheritance (Strongest) A ——▷ B
// 6. INHERITANCE (Strongest) - A is-a B
// Notation: A ——▷ B (solid arrow with hollow triangle)
class Animal { // Class B (superclass)
String species;
Animal({required this.species});
void breathe() {
print('$species is breathing');
}
}
class Dog extends Animal { // Class A (subclass)
String breed;
Dog({required this.breed}) : super(species: 'Canine');
void bark() {
print('$breed dog is barking');
}
}- What: A “is-a” B
- How: A inherits all properties and methods from B
- Coupling: Very strong - changes to B affect A
- Example: Dog extends Animal
4.2 Key Design Principles:
COUPLING STRENGTH EXPLANATION:
WEAKEST ←————————————————————————————————————————————————————————————→ STRONGEST
| |
Dependency → Association → Aggregation/Implementation → Composition → Inheritance
- Dependency: Loosest coupling, temporary usage
- Association: Permanent reference, but independent
- Aggregation: Whole-part relationship, parts can exist alone
- Implementation: Contract-based, moderate coupling
- Composition: Strong ownership, parts depend on whole
- Inheritance: Tightest coupling, child IS parent
- Prefer weaker relationships when possible for flexibility
- Use composition over inheritance for better modularity
- Dependencies are easier to test than associations
- Interfaces provide flexibility while maintaining contracts
- Inheritance should represent true “is-a” relationships
The diagram shows these relationships nested to indicate that stronger relationships often include characteristics of weaker ones (e.g., inheritance includes aspects of implementation and dependency).
