Association
Classes that know about each other and can communicate.
Association is a relationship where one class knows about another class and can use its services. It’s a “uses-a” or “knows-a” relationship where classes can exist independently.
What is Association?
Section titled “What is Association?”Association represents a relationship where:
- Classes know about each other
- Classes can communicate with each other
- Classes have independent lifecycles
- Usually implemented with references or pointers
Key Characteristics
Section titled “Key Characteristics”- Independent existence - Both classes can exist without each other
- Communication - Classes can call each other’s methods
- References - One class holds a reference to another
- Weaker than composition - No ownership implied
Unidirectional Association
Section titled “Unidirectional Association”One class knows about another, but not vice versa.
💡 Tip: Click dropdown to switch between languages
class Teacher: def __init__(self, name: str): self.name = name self.courses = [] # Association with Course
def teach(self, course): """Teacher uses Course""" self.courses.append(course) return f"{self.name} is teaching {course.name}"
class Course: def __init__(self, name: str): self.name = name
# Usageteacher = Teacher("Dr. Smith")course = Course("Python Programming")
print(teacher.teach(course)) # "Dr. Smith is teaching Python Programming"# Course doesn't know about Teacherpublic class Teacher { private String name; private java.util.List<Course> courses; // Association with Course
public Teacher(String name) { this.name = name; this.courses = new java.util.ArrayList<>(); }
// Teacher uses Course public String teach(Course course) { courses.add(course); return name + " is teaching " + course.getName(); }}
public class Course { private String name;
public Course(String name) { this.name = name; }
public String getName() { return name; }}
// Usagepublic class Main { public static void main(String[] args) { Teacher teacher = new Teacher("Dr. Smith"); Course course = new Course("Python Programming");
System.out.println(teacher.teach(course)); // "Dr. Smith is teaching Python Programming" // Course doesn't know about Teacher }}class Teacher { private name: string; private courses: Course[]; // Association with Course
constructor(name: string) { this.name = name; this.courses = []; }
teach(course: Course): string { this.courses.push(course); return `${this.name} is teaching ${course.getName()}`; }}
class Course { private name: string;
constructor(name: string) { this.name = name; }
getName(): string { return this.name; }}
// Usageconst teacher = new Teacher("Dr. Smith");const course = new Course("Python Programming");
console.log(teacher.teach(course)); // "Dr. Smith is teaching Python Programming"// Course doesn't know about Teacher#include <iostream>#include <string>#include <vector>
class Course {private: std::string name;
public: Course(const std::string& name) : name(name) {}
std::string getName() const { return name; }};
class Teacher {private: std::string name; std::vector<Course*> courses; // Association with Course
public: Teacher(const std::string& name) : name(name) {}
std::string teach(Course* course) { courses.push_back(course); return name + " is teaching " + course->getName(); }};
int main() { Teacher teacher("Dr. Smith"); Course course("Python Programming");
std::cout << teacher.teach(&course) << std::endl; // "Dr. Smith is teaching Python Programming" // Course doesn't know about Teacher
return 0;}using System;using System.Collections.Generic;
public class Teacher{ private string name; private List<Course> courses; // Association with Course
public Teacher(string name) { this.name = name; this.courses = new List<Course>(); }
public string Teach(Course course) { courses.Add(course); return $"{name} is teaching {course.GetName()}"; }}
public class Course{ private string name;
public Course(string name) { this.name = name; }
public string GetName() { return name; }}
class Program{ static void Main() { Teacher teacher = new Teacher("Dr. Smith"); Course course = new Course("Python Programming");
Console.WriteLine(teacher.Teach(course)); // "Dr. Smith is teaching Python Programming" // Course doesn't know about Teacher }}package main
import "fmt"
type Teacher struct { name string courses []*Course}
func NewTeacher(name string) *Teacher { return &Teacher{name: name}}
func (t *Teacher) Teach(course *Course) string { t.courses = append(t.courses, course) return fmt.Sprintf("%s is teaching %s", t.name, course.Name())}
type Course struct { name string}
func NewCourse(name string) *Course { return &Course{name: name}}
func (c *Course) Name() string { return c.name }
func main() { teacher := NewTeacher("Dr. Smith") course := NewCourse("Python Programming") fmt.Println(teacher.Teach(course)) // Course doesn't know about Teacher}struct Course { name: String,}
impl Course { fn new(name: impl Into<String>) -> Self { Self { name: name.into() } }
fn name(&self) -> &str { &self.name }}
struct Teacher<'a> { name: String, courses: Vec<&'a Course>, // Association with Course}
impl<'a> Teacher<'a> { fn new(name: impl Into<String>) -> Self { Self { name: name.into(), courses: Vec::new(), } }
fn teach(&mut self, course: &'a Course) -> String { self.courses.push(course); format!("{} is teaching {}", self.name, course.name()) }}
fn main() { let course = Course::new("Python Programming"); let mut teacher = Teacher::new("Dr. Smith");
println!("{}", teacher.teach(&course)); // Course doesn't know about Teacher}Bidirectional Association
Section titled “Bidirectional Association”Both classes know about each other.
💡 Tip: Click dropdown to switch between languages
class Student: def __init__(self, name: str): self.name = name self.courses = [] # Student knows about courses
def enroll(self, course): self.courses.append(course) course.students.append(self) # Course knows about student
class Course: def __init__(self, name: str): self.name = name self.students = [] # Course knows about students
# Usagestudent = Student("Alice")course = Course("Data Structures")
student.enroll(course)print(f"{student.name} enrolled in {course.name}")print(f"{course.name} has {len(course.students)} student(s)")public class Student { private String name; private java.util.List<Course> courses; // Student knows about courses
public Student(String name) { this.name = name; this.courses = new java.util.ArrayList<>(); }
public void enroll(Course course) { courses.add(course); course.addStudent(this); // Course knows about student }
public String getName() { return name; }}
public class Course { private String name; private java.util.List<Student> students; // Course knows about students
public Course(String name) { this.name = name; this.students = new java.util.ArrayList<>(); }
public void addStudent(Student student) { students.add(student); }
public String getName() { return name; }
public int getStudentCount() { return students.size(); }}
// Usagepublic class Main { public static void main(String[] args) { Student student = new Student("Alice"); Course course = new Course("Data Structures");
student.enroll(course); System.out.println(student.getName() + " enrolled in " + course.getName()); System.out.println(course.getName() + " has " + course.getStudentCount() + " student(s)"); }}class Student { private name: string; private courses: Course[]; // Student knows about courses
constructor(name: string) { this.name = name; this.courses = []; }
enroll(course: Course): void { this.courses.push(course); course.addStudent(this); // Course knows about student }
getName(): string { return this.name; }}
class Course { private name: string; private students: Student[]; // Course knows about students
constructor(name: string) { this.name = name; this.students = []; }
addStudent(student: Student): void { this.students.push(student); }
getName(): string { return this.name; }
getStudentCount(): number { return this.students.length; }}
// Usageconst student = new Student("Alice");const course = new Course("Data Structures");
student.enroll(course);console.log(`${student.getName()} enrolled in ${course.getName()}`);console.log(`${course.getName()} has ${course.getStudentCount()} student(s)`);#include <iostream>#include <string>#include <vector>
// Forward declarationclass Course;
class Student {private: std::string name; std::vector<Course*> courses; // Student knows about courses
public: Student(const std::string& name) : name(name) {}
void enroll(Course* course);
std::string getName() const { return name; }};
class Course {private: std::string name; std::vector<Student*> students; // Course knows about students
public: Course(const std::string& name) : name(name) {}
void addStudent(Student* student) { students.push_back(student); }
std::string getName() const { return name; }
int getStudentCount() const { return students.size(); }};
void Student::enroll(Course* course) { courses.push_back(course); course->addStudent(this); // Course knows about student}
int main() { Student student("Alice"); Course course("Data Structures");
student.enroll(&course); std::cout << student.getName() << " enrolled in " << course.getName() << std::endl; std::cout << course.getName() << " has " << course.getStudentCount() << " student(s)" << std::endl;
return 0;}using System;using System.Collections.Generic;
public class Student{ private string name; private List<Course> courses; // Student knows about courses
public Student(string name) { this.name = name; this.courses = new List<Course>(); }
public void Enroll(Course course) { courses.Add(course); course.AddStudent(this); // Course knows about student }
public string GetName() { return name; }}
public class Course{ private string name; private List<Student> students; // Course knows about students
public Course(string name) { this.name = name; this.students = new List<Student>(); }
public void AddStudent(Student student) { students.Add(student); }
public string GetName() { return name; }
public int GetStudentCount() { return students.Count; }}
class Program{ static void Main() { Student student = new Student("Alice"); Course course = new Course("Data Structures");
student.Enroll(course); Console.WriteLine($"{student.GetName()} enrolled in {course.GetName()}"); Console.WriteLine($"{course.GetName()} has {course.GetStudentCount()} student(s)"); }}package main
import "fmt"
type Student struct { name string courses []*Course}
func NewStudent(name string) *Student { return &Student{name: name}}
func (s *Student) Enroll(course *Course) { s.courses = append(s.courses, course) course.addStudent(s)}
func (s *Student) GetName() string { return s.name }
type Course struct { name string students []*Student}
func NewCourse(name string) *Course { return &Course{name: name}}
func (c *Course) addStudent(s *Student) { c.students = append(c.students, s)}
func (c *Course) GetName() string { return c.name }
func (c *Course) GetStudentCount() int { return len(c.students) }
func main() { student := NewStudent("Alice") course := NewCourse("Data Structures") student.Enroll(course) fmt.Printf("%s enrolled in %s\n", student.GetName(), course.GetName()) fmt.Printf("%s has %d student(s)\n", course.GetName(), course.GetStudentCount())}use std::cell::RefCell;use std::rc::{Rc, Weak};
struct Student { name: String, courses: Vec<Weak<RefCell<Course>>>,}
impl Student { fn new(name: impl Into<String>) -> Rc<RefCell<Self>> { Rc::new(RefCell::new(Self { name: name.into(), courses: Vec::new(), })) }
fn enroll(student: &Rc<RefCell<Student>>, course: &Rc<RefCell<Course>>) { student.borrow_mut().courses.push(Rc::downgrade(course)); course.borrow_mut().add_student(student); }}
struct Course { name: String, students: Vec<Weak<RefCell<Student>>>,}
impl Course { fn new(name: impl Into<String>) -> Rc<RefCell<Self>> { Rc::new(RefCell::new(Self { name: name.into(), students: Vec::new(), })) }
fn add_student(&mut self, student: &Rc<RefCell<Student>>) { self.students.push(Rc::downgrade(student)); }
fn student_count(&self) -> usize { self.students.len() }}
fn main() { let student = Student::new("Alice"); let course = Course::new("Data Structures");
Student::enroll(&student, &course); println!( "{} enrolled in {}", student.borrow().name, course.borrow().name ); println!( "{} has {} student(s)", course.borrow().name, course.borrow().student_count() );}Visual Representation
Section titled “Visual Representation”Real-World Example: Library System
Section titled “Real-World Example: Library System” 💡 Tip: Click dropdown to switch between languages
class Book: def __init__(self, isbn: str, title: str): self.isbn = isbn self.title = title self.available = True
class Loan: def __init__(self, member, book): self.member = member # Association - references Member self.book = book # Association - references Book self.borrow_date = None self.return_date = None
def borrow(self): if self.book.available: self.book.available = False self.borrow_date = "2024-01-01" return f"{self.member.name} borrowed {self.book.title}" return "Book not available"
class Member: def __init__(self, name: str, member_id: str): self.name = name self.member_id = member_id
def borrow_book(self, book): loan = Loan(self, book) # Creates association return loan.borrow()
# Usagemember = Member("Alice", "M001")book = Book("12345", "Python Guide")
print(member.borrow_book(book)) # "Alice borrowed Python Guide"public class Book { private String isbn; private String title; private boolean available;
public Book(String isbn, String title) { this.isbn = isbn; this.title = title; this.available = true; }
public String getTitle() { return title; }
public boolean isAvailable() { return available; }
public void setAvailable(boolean available) { this.available = available; }}
public class Loan { private Member member; // Association - references Member private Book book; // Association - references Book private String borrowDate; private String returnDate;
public Loan(Member member, Book book) { this.member = member; this.book = book; }
public String borrow() { if (book.isAvailable()) { book.setAvailable(false); this.borrowDate = "2024-01-01"; return member.getName() + " borrowed " + book.getTitle(); } return "Book not available"; }}
public class Member { private String name; private String memberId;
public Member(String name, String memberId) { this.name = name; this.memberId = memberId; }
public String getName() { return name; }
public String borrowBook(Book book) { Loan loan = new Loan(this, book); // Creates association return loan.borrow(); }}
// Usagepublic class Main { public static void main(String[] args) { Member member = new Member("Alice", "M001"); Book book = new Book("12345", "Python Guide");
System.out.println(member.borrowBook(book)); // "Alice borrowed Python Guide" }}class Book { private isbn: string; private title: string; public available: boolean;
constructor(isbn: string, title: string) { this.isbn = isbn; this.title = title; this.available = true; }
getTitle(): string { return this.title; }}
class Loan { private member: Member; // Association - references Member private book: Book; // Association - references Book private borrowDate?: string; private returnDate?: string;
constructor(member: Member, book: Book) { this.member = member; this.book = book; }
borrow(): string { if (this.book.available) { this.book.available = false; this.borrowDate = "2024-01-01"; return `${this.member.getName()} borrowed ${this.book.getTitle()}`; } return "Book not available"; }}
class Member { private name: string; private memberId: string;
constructor(name: string, memberId: string) { this.name = name; this.memberId = memberId; }
getName(): string { return this.name; }
borrowBook(book: Book): string { const loan = new Loan(this, book); // Creates association return loan.borrow(); }}
// Usageconst member = new Member("Alice", "M001");const book = new Book("12345", "Python Guide");
console.log(member.borrowBook(book)); // "Alice borrowed Python Guide"#include <iostream>#include <string>
class Book {private: std::string isbn; std::string title;
public: bool available;
Book(const std::string& isbn, const std::string& title) : isbn(isbn), title(title), available(true) {}
std::string getTitle() const { return title; }};
// Forward declarationsclass Member;
class Loan {private: Member* member; // Association - references Member Book* book; // Association - references Book std::string borrowDate; std::string returnDate;
public: Loan(Member* member, Book* book);
std::string borrow();};
class Member {private: std::string name; std::string memberId;
public: Member(const std::string& name, const std::string& memberId) : name(name), memberId(memberId) {}
std::string getName() const { return name; }
std::string borrowBook(Book* book) { Loan loan(this, book); // Creates association return loan.borrow(); }};
Loan::Loan(Member* member, Book* book) : member(member), book(book) {}
std::string Loan::borrow() { if (book->available) { book->available = false; borrowDate = "2024-01-01"; return member->getName() + " borrowed " + book->getTitle(); } return "Book not available";}
int main() { Member member("Alice", "M001"); Book book("12345", "Python Guide");
std::cout << member.borrowBook(&book) << std::endl; // "Alice borrowed Python Guide"
return 0;}using System;
public class Book{ private string isbn; private string title; public bool available;
public Book(string isbn, string title) { this.isbn = isbn; this.title = title; this.available = true; }
public string GetTitle() { return title; }}
public class Loan{ private Member member; // Association - references Member private Book book; // Association - references Book private string borrowDate; private string returnDate;
public Loan(Member member, Book book) { this.member = member; this.book = book; }
public string Borrow() { if (book.available) { book.available = false; borrowDate = "2024-01-01"; return $"{member.GetName()} borrowed {book.GetTitle()}"; } return "Book not available"; }}
public class Member{ private string name; private string memberId;
public Member(string name, string memberId) { this.name = name; this.memberId = memberId; }
public string GetName() { return name; }
public string BorrowBook(Book book) { Loan loan = new Loan(this, book); // Creates association return loan.Borrow(); }}
class Program{ static void Main() { Member member = new Member("Alice", "M001"); Book book = new Book("12345", "Python Guide");
Console.WriteLine(member.BorrowBook(book)); // "Alice borrowed Python Guide" }}package main
import "fmt"
type Book struct { ISBN string Title string Available bool}
func NewBook(isbn, title string) *Book { return &Book{ISBN: isbn, Title: title, Available: true}}
type Loan struct { member *Member book *Book borrowDate string}
func NewLoan(member *Member, book *Book) *Loan { return &Loan{member: member, book: book}}
func (l *Loan) Borrow() string { if l.book.Available { l.book.Available = false l.borrowDate = "2024-01-01" return fmt.Sprintf("%s borrowed %s", l.member.name, l.book.Title) } return "Book not available"}
type Member struct { name string memberID string}
func NewMember(name, memberID string) *Member { return &Member{name: name, memberID: memberID}}
func (m *Member) BorrowBook(book *Book) string { loan := NewLoan(m, book) return loan.Borrow()}
func main() { member := NewMember("Alice", "M001") book := NewBook("12345", "Python Guide") fmt.Println(member.BorrowBook(book))}struct Book { isbn: String, title: String, available: bool,}
impl Book { fn new(isbn: impl Into<String>, title: impl Into<String>) -> Self { Self { isbn: isbn.into(), title: title.into(), available: true, } }}
struct Loan<'a> { member: &'a Member, // Association - references Member book: &'a mut Book, // Association - references Book borrow_date: Option<&'static str>,}
impl<'a> Loan<'a> { fn new(member: &'a Member, book: &'a mut Book) -> Self { Self { member, book, borrow_date: None, } }
fn borrow(&mut self) -> String { if self.book.available { self.book.available = false; self.borrow_date = Some("2024-01-01"); return format!("{} borrowed {}", self.member.name, self.book.title); } "Book not available".to_string() }}
struct Member { name: String, member_id: String,}
impl Member { fn new(name: impl Into<String>, member_id: impl Into<String>) -> Self { Self { name: name.into(), member_id: member_id.into(), } }
fn borrow_book(&self, book: &mut Book) -> String { let mut loan = Loan::new(self, book); // Creates association loan.borrow() }}
fn main() { let member = Member::new("Alice", "M001"); let mut book = Book::new("12345", "Python Guide");
println!("{}", member.borrow_book(&mut book));}Key Takeaways
Section titled “Key Takeaways”When to Use Association
Section titled “When to Use Association”Use Association when:
- Classes need to communicate with each other
- Classes have independent lifecycles
- One class uses another’s services
- You need flexibility to change relationships
- Classes are loosely coupled
Examples:
- Teacher ↔ Course
- Student ↔ Course
- Order ↔ Product
- Loan ↔ Book
- Customer ↔ Order