4  OOP Relation

4.1 Summary of UML Relationships (Weakest to Strongest)

Figure 4.1: OOP Relations

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
  1. Prefer weaker relationships when possible for flexibility
  2. Use composition over inheritance for better modularity
  3. Dependencies are easier to test than associations
  4. Interfaces provide flexibility while maintaining contracts
  5. 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).