Skip to content

Session 8

Implementing class diagrams in code

Updated View as Markdown

Sessions 8 to 10 turn diagrams into code and tables. The mapping is mechanical once you know the rules: a class becomes a class, an attribute a field, an operation a method, and each association end a reference or a collection.

Objectives

Do not copy. Read for understanding and the viva
  • Complete questions 19 to 20 of the manual: implementing class diagrams in code
  • Prepare the deliverable before the lab and finish it during the session
  • Be ready to explain every step in the viva

Questions Covered

Do not copy. Read for understanding and the viva
QuestionRequirementStatus
Q19Implement the following Class Diagram in C++/JavaComplete
Q20Implement the Class Diagram of figure 1.18, in C++ or JavaComplete

Preparation

Do not copy. Read for understanding and the viva
  • The manual asks for a problem description of 300 to 500 words and a list of assumptions before every diagram. Write both first; they fix the scope the evaluator marks you against.
  • The manual says C++ or Java. Every program here is given in C++, Rust, Python and TypeScript, and any of the four is acceptable in the lab; pick one and keep it for all three sessions.
  • Map multiplicity: 1 becomes a single reference, * becomes a List (Java) or std::vector (C++).
  • Write a small main that creates a few objects and exercises every operation so the program actually runs.

Question 19

Problem Statement

Write in lab record

Implement the following Class Diagram in C++/Java.

Class diagram for Student Registration: University, School, Faculty, Programme, Course, Student

Figure 1.15: Class Diagram for Student Registration

Solution

Write in lab record

Assumptions

The system keeps the academic structure of one university. A university has a name, an address and a phone number and is made up of one or more schools. Each school is named and is responsible for a set of faculty members and a set of programmes. A faculty member has an ID, a name and the name of the school that employs them; at most one other faculty member is their head of department. Faculty members teach courses. A programme has an ID and a name, and a course has a code and a name. Students register with the university; each student has a roll number, a name and an address and enrols in a programme. The program must let an administrator add and remove schools and students at university level, add and remove faculty and programmes at school level, keep a searchable catalogue of courses, and look up a school by name, a student by roll number and a course by name or code. Every relationship in the figure must be navigable from the code in the direction the figure shows.

  • The reference listing is C++17, compiled with clang++ -std=c++17 -Wall -Wextra; the same classes, links and main are given in Rust, Python and TypeScript, and all four print the same output.
  • Objects are created in main and linked with pointers; no object owns another, so there is no new or delete.
  • A 1 or 0..1 end is a pointer; a * or 1..* end is a std::vector of pointers.
  • The four operations shown inside Course (addCourse, removeCourse, getCoursebyName, getCoursebyCode) manage the list of all courses, so they are static and work on one shared catalogue.
  • The aggregation between Course and Programme is implemented exactly as drawn: the diamond is on Course with multiplicity 1, and * is on Programme, so Course holds the vector of programmes.
  • The Teaches arrow points from Faculty to Course, so only Faculty holds the link. The enrol arrow points from Student to Programme, so only Student holds the link.
  • The constraint one student per programme on the registration link is read as: one registration binds a student to exactly one programme. Student::enrol refuses a second programme.
  • getAllSchool returns the vector of schools; getSchool and getStudent return nullptr when nothing matches.

Diagram elements

ClassAttributesOperationsAssociation ends
Universityname: String, address: String, phone: integeraddSchool, removeSchool, addStudent, removeStudent, getSchool, getAllSchool, getStudenthas 1 to 1..* School (aggregation); registration 1 to * Student (aggregation, constraint one student per programme)
Schoolname: StringaddFaculty, removeFaculty, addProgramme, removeProgrammeAssignTo 1 to 1..* Faculty (aggregation); 1..* to 1..* Programme
FacultyfacultyID: String, facultyName: String, schoolName: StringnoneHOD 0..1 to Faculty (reflexive); Teaches 1..* Faculty to Course (arrow to Course)
ProgrammeprogramID, ProgramNamenone* Programme to 1 Course (diamond on Course)
CoursecourseCode, courseNameaddCourse, removeCourse, getCoursebyName, getCoursebyCodesee Programme and Faculty
StudentstuID: integer, name: String, address: Stringnoneenrol Student to Programme (arrow to Programme)

Steps

  1. Create the folder session-8 and save the listing below as student_registration.cpp.
  2. Compile: clang++ -std=c++17 -Wall -Wextra -o student_registration student_registration.cpp. The build must print nothing.
  3. Run ./student_registration and paste the output into the record.
  4. For another language, save the matching tab and run it: rustc -O --edition 2021 student_registration.rs && ./student_registration, python3 student_registration.py, or node student_registration.ts (Node 22.18 or later strips the types natively, no compiler needed).

Program

Lab record: write one language only. Pick yours once and every page opens on it; the other tabs are the same solution for comparison.

student_registration.cppcpp
// student_registration.cpp -- MCSL-222 Session 8, Q19
// Figure 1.15 (Student Registration) implemented in C++17.
// Build: clang++ -std=c++17 -Wall -Wextra -o student_registration student_registration.cpp
//
// Mapping used throughout:
//   class            -> class
//   attribute        -> public data member (same name as the figure)
//   operation        -> member function (same name as the figure)
//   association end  -> pointer for 1 / 0..1, std::vector of pointers for * / 1..*
// Objects are created in main and never deleted here; the pointers are links only.

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

class School;
class Faculty;
class Programme;
class Course;
class Student;

// Removes one link from an association vector. Every remove* operation uses it.
template <class T>
static void unlink(std::vector<T*>& v, T* p) {
    v.erase(std::remove(v.begin(), v.end(), p), v.end());
}

// ---------------------------------------------------------------- Course
class Course {
public:
    std::string courseCode;
    std::string courseName;
    std::vector<Programme*> programmes;  // Course (1) <>-- (*) Programme, as drawn

    Course(std::string code, std::string name)
        : courseCode(std::move(code)), courseName(std::move(name)) {}

    // The four operations in the figure manage the list of courses, so they
    // work on one shared catalogue rather than on a single Course object.
    inline static std::vector<Course*> catalogue;

    static void addCourse(Course& c) { catalogue.push_back(&c); }
    static void removeCourse(Course& c) { unlink(catalogue, &c); }
    static Course* getCoursebyName(const std::string& name) {
        for (Course* c : catalogue)
            if (c->courseName == name) return c;
        return nullptr;
    }
    static Course* getCoursebyCode(const std::string& code) {
        for (Course* c : catalogue)
            if (c->courseCode == code) return c;
        return nullptr;
    }
};

// ------------------------------------------------------------- Programme
class Programme {
public:
    std::string programID;
    std::string ProgramName;
    std::vector<School*> schools;  // School (1..*) --- (1..*) Programme

    Programme(std::string id, std::string name)
        : programID(std::move(id)), ProgramName(std::move(name)) {}
};

// --------------------------------------------------------------- Faculty
class Faculty {
public:
    std::string facultyID;
    std::string facultyName;
    std::string schoolName;
    Faculty* hod = nullptr;         // HOD 0..1 (reflexive association)
    std::vector<Course*> courses;   // Teaches: Faculty (1..*) --> Course

    Faculty(std::string id, std::string name)
        : facultyID(std::move(id)), facultyName(std::move(name)) {}

    void teaches(Course& c) { courses.push_back(&c); }
};

// ---------------------------------------------------------------- School
class School {
public:
    std::string name;
    std::vector<Faculty*> faculties;     // AssignTo: School (1) <>-- (1..*) Faculty
    std::vector<Programme*> programmes;  // School (1..*) --- (1..*) Programme

    explicit School(std::string n) : name(std::move(n)) {}

    void addFaculty(Faculty& f) {
        faculties.push_back(&f);
        f.schoolName = name;  // the figure keeps the school name inside Faculty
    }
    void removeFaculty(Faculty& f) {
        unlink(faculties, &f);
        f.schoolName.clear();
    }
    void addProgramme(Programme& p) {  // both ends are 1..*, so update both
        programmes.push_back(&p);
        p.schools.push_back(this);
    }
    void removeProgramme(Programme& p) {
        unlink(programmes, &p);
        unlink(p.schools, this);
    }
};

// --------------------------------------------------------------- Student
class Student {
public:
    int stuID;
    std::string name;
    std::string address;
    Programme* programme = nullptr;  // enrol: Student --> Programme

    Student(int id, std::string n, std::string addr)
        : stuID(id), name(std::move(n)), address(std::move(addr)) {}

    // Constraint {one student per programme}: a registration binds a student
    // to exactly one programme, so a second enrol() is refused.
    bool enrol(Programme& p) {
        if (programme != nullptr) {
            std::cout << "  refused: " << name << " is already enrolled in "
                      << programme->ProgramName << " (one student per programme)\n";
            return false;
        }
        programme = &p;
        return true;
    }
};

// ------------------------------------------------------------ University
class University {
public:
    std::string name;
    std::string address;
    int phone;
    std::vector<School*> schools;    // has: University (1) <>-- (1..*) School
    std::vector<Student*> students;  // registration: University (1) <>-- (*) Student

    University(std::string n, std::string addr, int ph)
        : name(std::move(n)), address(std::move(addr)), phone(ph) {}

    void addSchool(School& s) { schools.push_back(&s); }
    void removeSchool(School& s) { unlink(schools, &s); }
    void addStudent(Student& s) { students.push_back(&s); }
    void removeStudent(Student& s) { unlink(students, &s); }
    School* getSchool(const std::string& n) const {
        for (School* s : schools)
            if (s->name == n) return s;
        return nullptr;
    }
    const std::vector<School*>& getAllSchool() const { return schools; }
    Student* getStudent(int id) const {
        for (Student* s : students)
            if (s->stuID == id) return s;
        return nullptr;
    }
};

// ------------------------------------------------------------------ main
static void printUniversity(const University& u) {
    std::cout << u.name << ", " << u.address << ", phone " << u.phone << "\n";
    for (const School* s : u.getAllSchool()) {
        std::cout << "  School: " << s->name << "\n";
        for (const Faculty* f : s->faculties) {
            std::cout << "    Faculty " << f->facultyID << " " << f->facultyName
                      << " (school " << f->schoolName << ")"
                      << (f->hod ? ", HOD " + f->hod->facultyName : "") << " teaches";
            for (const Course* c : f->courses) std::cout << " " << c->courseCode;
            std::cout << "\n";
        }
        for (const Programme* p : s->programmes)
            std::cout << "    Programme " << p->programID << " " << p->ProgramName << "\n";
    }
    for (const Student* st : u.students)
        std::cout << "  Student " << st->stuID << " " << st->name << ", " << st->address
                  << " -> " << (st->programme ? st->programme->ProgramName : "not enrolled")
                  << "\n";
}

int main() {
    University ignou("IGNOU", "Maidan Garhi, New Delhi", 29572514);

    // Two schools
    School socis("SOCIS");
    School soms("SOMS");
    ignou.addSchool(socis);
    ignou.addSchool(soms);

    // Faculties, with a HOD link inside SOCIS
    Faculty f1("F01", "Dr. Sharma");
    Faculty f2("F02", "Dr. Verma");
    Faculty f3("F03", "Dr. Iyer");
    socis.addFaculty(f1);
    socis.addFaculty(f2);
    soms.addFaculty(f3);
    f2.hod = &f1;  // HOD 0..1

    // Programmes
    Programme mca("MCA", "Master of Computer Applications");
    Programme mba("MBA", "Master of Business Administration");
    socis.addProgramme(mca);
    soms.addProgramme(mba);

    // Courses and the catalogue operations
    Course c1("MCS-217", "Software Engineering");
    Course c2("MCSL-222", "OOAD and Web Technologies Lab");
    Course c3("MMPC-001", "Management Concepts");
    Course::addCourse(c1);
    Course::addCourse(c2);
    Course::addCourse(c3);
    c1.programmes.push_back(&mca);  // Course (1) <>-- (*) Programme
    c2.programmes.push_back(&mca);
    c3.programmes.push_back(&mba);
    f1.teaches(c1);
    f2.teaches(c2);
    f3.teaches(c3);

    // Students and registration
    Student s1(2401, "Asha", "Jaipur");
    Student s2(2402, "Ravi", "Pune");
    ignou.addStudent(s1);
    ignou.addStudent(s2);
    s1.enrol(mca);
    s2.enrol(mba);

    std::cout << "--- after setup ---\n";
    printUniversity(ignou);

    std::cout << "--- constraint check ---\n";
    s1.enrol(mba);  // refused: already in MCA

    std::cout << "--- lookups ---\n";
    std::cout << "getSchool(\"SOMS\") -> " << ignou.getSchool("SOMS")->name << "\n";
    std::cout << "getSchool(\"SOL\") -> "
              << (ignou.getSchool("SOL") ? "found" : "nullptr") << "\n";
    std::cout << "getStudent(2402) -> " << ignou.getStudent(2402)->name << "\n";
    std::cout << "getCoursebyCode(\"MCSL-222\") -> "
              << Course::getCoursebyCode("MCSL-222")->courseName << "\n";
    std::cout << "getCoursebyName(\"Management Concepts\") -> "
              << Course::getCoursebyName("Management Concepts")->courseCode << "\n";
    std::cout << "catalogue size: " << Course::catalogue.size() << "\n";

    std::cout << "--- removals ---\n";
    Course::removeCourse(c3);
    socis.removeFaculty(f2);
    soms.removeProgramme(mba);
    ignou.removeStudent(s2);
    ignou.removeSchool(soms);
    std::cout << "catalogue size: " << Course::catalogue.size()
              << ", f2.schoolName is \"" << f2.schoolName << "\""
              << ", mba.schools.size() = " << mba.schools.size() << "\n";
    printUniversity(ignou);
    return 0;
}
student_registration.rsrust
// student_registration.rs -- MCSL-222 Session 8, Q19
// Figure 1.15 (Student Registration) in Rust 2021, standard library only.
// Ownership: every object is an Rc<RefCell<T>>; an association end is an Rc clone (Weak for the School back link inside Programme, so the 1..* to 1..* link is not an Rc cycle).
// Build: rustc -O --edition 2021 student_registration.rs && ./student_registration
//
// Mapping used throughout:
//   class            -> struct + impl
//   attribute        -> pub field (same name as the figure)
//   operation        -> method or associated function (same name as the figure)
//   association end  -> Option<Rc<..>> for 1 / 0..1, Vec<Rc<..>> for * / 1..*
#![allow(non_snake_case)] // attribute and operation names are kept exactly as in the figure
#![allow(dead_code)] // Course.programmes is an association end main fills but never reads

use std::cell::RefCell;
use std::rc::{Rc, Weak};

type Ref<T> = Rc<RefCell<T>>;

fn new_ref<T>(x: T) -> Ref<T> {
    Rc::new(RefCell::new(x))
}

// Removes one link from an association vector. Every remove* operation uses it.
fn unlink<T>(v: &mut Vec<Ref<T>>, p: &Ref<T>) {
    v.retain(|x| !Rc::ptr_eq(x, p));
}

// ---------------------------------------------------------------- Course
struct Course {
    courseCode: String,
    courseName: String,
    programmes: Vec<Ref<Programme>>, // Course (1) <>-- (*) Programme, as drawn
}

thread_local! {
    // The four operations in the figure manage the list of courses, so they
    // work on one shared catalogue: the C++ static member is a thread-local static here.
    static CATALOGUE: RefCell<Vec<Ref<Course>>> = RefCell::new(Vec::new());
}

impl Course {
    fn new(code: &str, name: &str) -> Ref<Course> {
        new_ref(Course { courseCode: code.into(), courseName: name.into(), programmes: Vec::new() })
    }
    fn addCourse(c: &Ref<Course>) {
        CATALOGUE.with(|cat| cat.borrow_mut().push(Rc::clone(c)));
    }
    fn removeCourse(c: &Ref<Course>) {
        CATALOGUE.with(|cat| unlink(&mut cat.borrow_mut(), c));
    }
    fn getCoursebyName(name: &str) -> Option<Ref<Course>> {
        CATALOGUE.with(|cat| cat.borrow().iter().find(|c| c.borrow().courseName == name).cloned())
    }
    fn getCoursebyCode(code: &str) -> Option<Ref<Course>> {
        CATALOGUE.with(|cat| cat.borrow().iter().find(|c| c.borrow().courseCode == code).cloned())
    }
    fn catalogueSize() -> usize {
        CATALOGUE.with(|cat| cat.borrow().len())
    }
}

// ------------------------------------------------------------- Programme
struct Programme {
    programID: String,
    ProgramName: String,
    schools: Vec<Weak<RefCell<School>>>, // School (1..*) --- (1..*) Programme, back end
}

impl Programme {
    fn new(id: &str, name: &str) -> Ref<Programme> {
        new_ref(Programme { programID: id.into(), ProgramName: name.into(), schools: Vec::new() })
    }
}

// --------------------------------------------------------------- Faculty
struct Faculty {
    facultyID: String,
    facultyName: String,
    schoolName: String,
    hod: Option<Ref<Faculty>>, // HOD 0..1 (reflexive association)
    courses: Vec<Ref<Course>>, // Teaches: Faculty (1..*) --> Course
}

impl Faculty {
    fn new(id: &str, name: &str) -> Ref<Faculty> {
        new_ref(Faculty {
            facultyID: id.into(),
            facultyName: name.into(),
            schoolName: String::new(),
            hod: None,
            courses: Vec::new(),
        })
    }
    fn teaches(&mut self, c: &Ref<Course>) {
        self.courses.push(Rc::clone(c));
    }
}

// ---------------------------------------------------------------- School
struct School {
    name: String,
    faculties: Vec<Ref<Faculty>>,    // AssignTo: School (1) <>-- (1..*) Faculty
    programmes: Vec<Ref<Programme>>, // School (1..*) --- (1..*) Programme
}

impl School {
    fn new(n: &str) -> Ref<School> {
        new_ref(School { name: n.into(), faculties: Vec::new(), programmes: Vec::new() })
    }
    fn addFaculty(&mut self, f: &Ref<Faculty>) {
        self.faculties.push(Rc::clone(f));
        f.borrow_mut().schoolName = self.name.clone(); // the figure keeps the school name inside Faculty
    }
    fn removeFaculty(&mut self, f: &Ref<Faculty>) {
        unlink(&mut self.faculties, f);
        f.borrow_mut().schoolName.clear();
    }
    // Both ends are 1..*, so update both. The back link needs this school's Rc,
    // which a &self method cannot reach, so these two take it as a parameter.
    fn addProgramme(this: &Ref<School>, p: &Ref<Programme>) {
        this.borrow_mut().programmes.push(Rc::clone(p));
        p.borrow_mut().schools.push(Rc::downgrade(this));
    }
    fn removeProgramme(this: &Ref<School>, p: &Ref<Programme>) {
        unlink(&mut this.borrow_mut().programmes, p);
        let me = Rc::downgrade(this);
        p.borrow_mut().schools.retain(|w| !Weak::ptr_eq(w, &me));
    }
}

// --------------------------------------------------------------- Student
struct Student {
    stuID: i32,
    name: String,
    address: String,
    programme: Option<Ref<Programme>>, // enrol: Student --> Programme
}

impl Student {
    fn new(id: i32, n: &str, addr: &str) -> Ref<Student> {
        new_ref(Student { stuID: id, name: n.into(), address: addr.into(), programme: None })
    }
    // Constraint {one student per programme}: a registration binds a student
    // to exactly one programme, so a second enrol() is refused.
    fn enrol(&mut self, p: &Ref<Programme>) -> bool {
        if let Some(cur) = &self.programme {
            println!(
                "  refused: {} is already enrolled in {} (one student per programme)",
                self.name,
                cur.borrow().ProgramName
            );
            return false;
        }
        self.programme = Some(Rc::clone(p));
        true
    }
}

// ------------------------------------------------------------ University
struct University {
    name: String,
    address: String,
    phone: i32,
    schools: Vec<Ref<School>>,   // has: University (1) <>-- (1..*) School
    students: Vec<Ref<Student>>, // registration: University (1) <>-- (*) Student
}

impl University {
    fn new(n: &str, addr: &str, ph: i32) -> University {
        University { name: n.into(), address: addr.into(), phone: ph, schools: Vec::new(), students: Vec::new() }
    }
    fn addSchool(&mut self, s: &Ref<School>) {
        self.schools.push(Rc::clone(s));
    }
    fn removeSchool(&mut self, s: &Ref<School>) {
        unlink(&mut self.schools, s);
    }
    fn addStudent(&mut self, s: &Ref<Student>) {
        self.students.push(Rc::clone(s));
    }
    fn removeStudent(&mut self, s: &Ref<Student>) {
        unlink(&mut self.students, s);
    }
    fn getSchool(&self, n: &str) -> Option<Ref<School>> {
        self.schools.iter().find(|s| s.borrow().name == n).cloned()
    }
    fn getAllSchool(&self) -> &Vec<Ref<School>> {
        &self.schools
    }
    fn getStudent(&self, id: i32) -> Option<Ref<Student>> {
        self.students.iter().find(|s| s.borrow().stuID == id).cloned()
    }
}

// ------------------------------------------------------------------ main
fn print_university(u: &University) {
    println!("{}, {}, phone {}", u.name, u.address, u.phone);
    for s in u.getAllSchool() {
        let s = s.borrow();
        println!("  School: {}", s.name);
        for f in &s.faculties {
            let f = f.borrow();
            let hod = match &f.hod {
                Some(h) => format!(", HOD {}", h.borrow().facultyName),
                None => String::new(),
            };
            let mut line = format!(
                "    Faculty {} {} (school {}){} teaches",
                f.facultyID, f.facultyName, f.schoolName, hod
            );
            for c in &f.courses {
                line.push(' ');
                line.push_str(&c.borrow().courseCode);
            }
            println!("{}", line);
        }
        for p in &s.programmes {
            let p = p.borrow();
            println!("    Programme {} {}", p.programID, p.ProgramName);
        }
    }
    for st in &u.students {
        let st = st.borrow();
        let prog = match &st.programme {
            Some(p) => p.borrow().ProgramName.clone(),
            None => "not enrolled".to_string(),
        };
        println!("  Student {} {}, {} -> {}", st.stuID, st.name, st.address, prog);
    }
}

fn main() {
    let mut ignou = University::new("IGNOU", "Maidan Garhi, New Delhi", 29572514);

    // Two schools
    let socis = School::new("SOCIS");
    let soms = School::new("SOMS");
    ignou.addSchool(&socis);
    ignou.addSchool(&soms);

    // Faculties, with a HOD link inside SOCIS
    let f1 = Faculty::new("F01", "Dr. Sharma");
    let f2 = Faculty::new("F02", "Dr. Verma");
    let f3 = Faculty::new("F03", "Dr. Iyer");
    socis.borrow_mut().addFaculty(&f1);
    socis.borrow_mut().addFaculty(&f2);
    soms.borrow_mut().addFaculty(&f3);
    f2.borrow_mut().hod = Some(Rc::clone(&f1)); // HOD 0..1

    // Programmes
    let mca = Programme::new("MCA", "Master of Computer Applications");
    let mba = Programme::new("MBA", "Master of Business Administration");
    School::addProgramme(&socis, &mca);
    School::addProgramme(&soms, &mba);

    // Courses and the catalogue operations
    let c1 = Course::new("MCS-217", "Software Engineering");
    let c2 = Course::new("MCSL-222", "OOAD and Web Technologies Lab");
    let c3 = Course::new("MMPC-001", "Management Concepts");
    Course::addCourse(&c1);
    Course::addCourse(&c2);
    Course::addCourse(&c3);
    c1.borrow_mut().programmes.push(Rc::clone(&mca)); // Course (1) <>-- (*) Programme
    c2.borrow_mut().programmes.push(Rc::clone(&mca));
    c3.borrow_mut().programmes.push(Rc::clone(&mba));
    f1.borrow_mut().teaches(&c1);
    f2.borrow_mut().teaches(&c2);
    f3.borrow_mut().teaches(&c3);

    // Students and registration
    let s1 = Student::new(2401, "Asha", "Jaipur");
    let s2 = Student::new(2402, "Ravi", "Pune");
    ignou.addStudent(&s1);
    ignou.addStudent(&s2);
    s1.borrow_mut().enrol(&mca);
    s2.borrow_mut().enrol(&mba);

    println!("--- after setup ---");
    print_university(&ignou);

    println!("--- constraint check ---");
    s1.borrow_mut().enrol(&mba); // refused: already in MCA

    println!("--- lookups ---");
    let soms_found = ignou.getSchool("SOMS").unwrap();
    println!("getSchool(\"SOMS\") -> {}", soms_found.borrow().name);
    // "nullptr" is printed for a miss so the output matches the C++ version.
    println!("getSchool(\"SOL\") -> {}", if ignou.getSchool("SOL").is_some() { "found" } else { "nullptr" });
    let ravi = ignou.getStudent(2402).unwrap();
    println!("getStudent(2402) -> {}", ravi.borrow().name);
    let by_code = Course::getCoursebyCode("MCSL-222").unwrap();
    println!("getCoursebyCode(\"MCSL-222\") -> {}", by_code.borrow().courseName);
    let by_name = Course::getCoursebyName("Management Concepts").unwrap();
    println!("getCoursebyName(\"Management Concepts\") -> {}", by_name.borrow().courseCode);
    println!("catalogue size: {}", Course::catalogueSize());

    println!("--- removals ---");
    Course::removeCourse(&c3);
    socis.borrow_mut().removeFaculty(&f2);
    School::removeProgramme(&soms, &mba);
    ignou.removeStudent(&s2);
    ignou.removeSchool(&soms);
    println!(
        "catalogue size: {}, f2.schoolName is \"{}\", mba.schools.size() = {}",
        Course::catalogueSize(),
        f2.borrow().schoolName,
        mba.borrow().schools.len()
    );
    print_university(&ignou);
}
student_registration.pypython
# student_registration.py -- MCSL-222 Session 8, Q19
# Figure 1.15 (Student Registration) in Python 3, standard library only.
# Run: python3 student_registration.py
#
# Mapping used throughout:
#   class            -> @dataclass
#   attribute        -> field (same name as the figure)
#   operation        -> method or @staticmethod (same name as the figure)
#   association end  -> object reference (or None) for 1 / 0..1, list for * / 1..*
# eq=False keeps identity comparison, so list.remove() unlinks that exact object
# and the two-way School/Programme links cannot recurse through a field-by-field ==.
from __future__ import annotations

from dataclasses import dataclass, field
from typing import ClassVar, Optional


# ---------------------------------------------------------------- Course
@dataclass(eq=False)
class Course:
    courseCode: str
    courseName: str
    programmes: list[Programme] = field(default_factory=list)  # Course (1) <>-- (*) Programme, as drawn

    # The four operations in the figure manage the list of courses, so they
    # work on one shared catalogue: a class attribute, the Python static member.
    catalogue: ClassVar[list[Course]] = []

    @staticmethod
    def addCourse(c: Course) -> None:
        Course.catalogue.append(c)

    @staticmethod
    def removeCourse(c: Course) -> None:
        Course.catalogue.remove(c)

    @staticmethod
    def getCoursebyName(name: str) -> Optional[Course]:
        return next((c for c in Course.catalogue if c.courseName == name), None)

    @staticmethod
    def getCoursebyCode(code: str) -> Optional[Course]:
        return next((c for c in Course.catalogue if c.courseCode == code), None)


# ------------------------------------------------------------- Programme
@dataclass(eq=False)
class Programme:
    programID: str
    ProgramName: str
    schools: list[School] = field(default_factory=list)  # School (1..*) --- (1..*) Programme


# --------------------------------------------------------------- Faculty
@dataclass(eq=False)
class Faculty:
    facultyID: str
    facultyName: str
    schoolName: str = ""
    hod: Optional[Faculty] = None                        # HOD 0..1 (reflexive association)
    courses: list[Course] = field(default_factory=list)  # Teaches: Faculty (1..*) --> Course

    def teaches(self, c: Course) -> None:
        self.courses.append(c)


# ---------------------------------------------------------------- School
@dataclass(eq=False)
class School:
    name: str
    faculties: list[Faculty] = field(default_factory=list)    # AssignTo: School (1) <>-- (1..*) Faculty
    programmes: list[Programme] = field(default_factory=list)  # School (1..*) --- (1..*) Programme

    def addFaculty(self, f: Faculty) -> None:
        self.faculties.append(f)
        f.schoolName = self.name  # the figure keeps the school name inside Faculty

    def removeFaculty(self, f: Faculty) -> None:
        self.faculties.remove(f)
        f.schoolName = ""

    def addProgramme(self, p: Programme) -> None:  # both ends are 1..*, so update both
        self.programmes.append(p)
        p.schools.append(self)

    def removeProgramme(self, p: Programme) -> None:
        self.programmes.remove(p)
        p.schools.remove(self)


# --------------------------------------------------------------- Student
@dataclass(eq=False)
class Student:
    stuID: int
    name: str
    address: str
    programme: Optional[Programme] = None  # enrol: Student --> Programme

    # Constraint {one student per programme}: a registration binds a student
    # to exactly one programme, so a second enrol() is refused.
    def enrol(self, p: Programme) -> bool:
        if self.programme is not None:
            print(f"  refused: {self.name} is already enrolled in "
                  f"{self.programme.ProgramName} (one student per programme)")
            return False
        self.programme = p
        return True


# ------------------------------------------------------------ University
@dataclass(eq=False)
class University:
    name: str
    address: str
    phone: int
    schools: list[School] = field(default_factory=list)    # has: University (1) <>-- (1..*) School
    students: list[Student] = field(default_factory=list)  # registration: University (1) <>-- (*) Student

    def addSchool(self, s: School) -> None:
        self.schools.append(s)

    def removeSchool(self, s: School) -> None:
        self.schools.remove(s)

    def addStudent(self, s: Student) -> None:
        self.students.append(s)

    def removeStudent(self, s: Student) -> None:
        self.students.remove(s)

    def getSchool(self, n: str) -> Optional[School]:
        return next((s for s in self.schools if s.name == n), None)

    def getAllSchool(self) -> list[School]:
        return self.schools

    def getStudent(self, id: int) -> Optional[Student]:
        return next((s for s in self.students if s.stuID == id), None)


# ------------------------------------------------------------------ main
def printUniversity(u: University) -> None:
    print(f"{u.name}, {u.address}, phone {u.phone}")
    for s in u.getAllSchool():
        print(f"  School: {s.name}")
        for f in s.faculties:
            hod = f", HOD {f.hod.facultyName}" if f.hod else ""
            codes = "".join(f" {c.courseCode}" for c in f.courses)
            print(f"    Faculty {f.facultyID} {f.facultyName} (school {f.schoolName}){hod} teaches{codes}")
        for p in s.programmes:
            print(f"    Programme {p.programID} {p.ProgramName}")
    for st in u.students:
        prog = st.programme.ProgramName if st.programme else "not enrolled"
        print(f"  Student {st.stuID} {st.name}, {st.address} -> {prog}")


def main() -> None:
    ignou = University("IGNOU", "Maidan Garhi, New Delhi", 29572514)

    # Two schools
    socis = School("SOCIS")
    soms = School("SOMS")
    ignou.addSchool(socis)
    ignou.addSchool(soms)

    # Faculties, with a HOD link inside SOCIS
    f1 = Faculty("F01", "Dr. Sharma")
    f2 = Faculty("F02", "Dr. Verma")
    f3 = Faculty("F03", "Dr. Iyer")
    socis.addFaculty(f1)
    socis.addFaculty(f2)
    soms.addFaculty(f3)
    f2.hod = f1  # HOD 0..1

    # Programmes
    mca = Programme("MCA", "Master of Computer Applications")
    mba = Programme("MBA", "Master of Business Administration")
    socis.addProgramme(mca)
    soms.addProgramme(mba)

    # Courses and the catalogue operations
    c1 = Course("MCS-217", "Software Engineering")
    c2 = Course("MCSL-222", "OOAD and Web Technologies Lab")
    c3 = Course("MMPC-001", "Management Concepts")
    Course.addCourse(c1)
    Course.addCourse(c2)
    Course.addCourse(c3)
    c1.programmes.append(mca)  # Course (1) <>-- (*) Programme
    c2.programmes.append(mca)
    c3.programmes.append(mba)
    f1.teaches(c1)
    f2.teaches(c2)
    f3.teaches(c3)

    # Students and registration
    s1 = Student(2401, "Asha", "Jaipur")
    s2 = Student(2402, "Ravi", "Pune")
    ignou.addStudent(s1)
    ignou.addStudent(s2)
    s1.enrol(mca)
    s2.enrol(mba)

    print("--- after setup ---")
    printUniversity(ignou)

    print("--- constraint check ---")
    s1.enrol(mba)  # refused: already in MCA

    print("--- lookups ---")
    print(f'getSchool("SOMS") -> {ignou.getSchool("SOMS").name}')
    # "nullptr" is printed for a miss so the output matches the C++ version.
    print(f'getSchool("SOL") -> {"found" if ignou.getSchool("SOL") else "nullptr"}')
    print(f"getStudent(2402) -> {ignou.getStudent(2402).name}")
    print(f'getCoursebyCode("MCSL-222") -> {Course.getCoursebyCode("MCSL-222").courseName}')
    print(f'getCoursebyName("Management Concepts") -> {Course.getCoursebyName("Management Concepts").courseCode}')
    print(f"catalogue size: {len(Course.catalogue)}")

    print("--- removals ---")
    Course.removeCourse(c3)
    socis.removeFaculty(f2)
    soms.removeProgramme(mba)
    ignou.removeStudent(s2)
    ignou.removeSchool(soms)
    print(f'catalogue size: {len(Course.catalogue)}, f2.schoolName is "{f2.schoolName}", '
          f"mba.schools.size() = {len(mba.schools)}")
    printUniversity(ignou)


if __name__ == "__main__":
    main()
student_registration.tsts
// student_registration.ts -- MCSL-222 Session 8, Q19
// Figure 1.15 (Student Registration) in TypeScript, no dependencies.
// Run: node student_registration.ts   (Node 22.18 or later strips the types itself)
//
// Mapping used throughout:
//   class            -> class
//   attribute        -> typed field (same name as the figure), readonly when it never changes
//   operation        -> method or static method (same name as the figure)
//   association end  -> `T | null` for 1 / 0..1, `T[]` for * / 1..*
"use strict";

// Removes one link from an association array. Every remove* operation uses it.
function unlink<T>(arr: T[], p: T): void {
  const i = arr.indexOf(p);
  if (i >= 0) arr.splice(i, 1);
}

// Student and University both carry a name and a postal address.
interface Addressed {
  readonly name: string;
  readonly address: string;
}

// ---------------------------------------------------------------- Course
class Course {
  // The four operations in the figure manage the list of courses, so they
  // work on one shared catalogue: a static field plus static methods.
  static readonly catalogue: Course[] = [];

  readonly courseCode: string;
  readonly courseName: string;
  readonly programmes: Programme[] = []; // Course (1) <>-- (*) Programme, as drawn

  constructor(code: string, name: string) {
    this.courseCode = code;
    this.courseName = name;
  }

  static addCourse(c: Course): void { Course.catalogue.push(c); }
  static removeCourse(c: Course): void { unlink(Course.catalogue, c); }
  static getCoursebyName(name: string): Course | null { return Course.catalogue.find((c) => c.courseName === name) ?? null; }
  static getCoursebyCode(code: string): Course | null { return Course.catalogue.find((c) => c.courseCode === code) ?? null; }
}

// ------------------------------------------------------------- Programme
class Programme {
  readonly programID: string;
  readonly ProgramName: string;
  readonly schools: School[] = []; // School (1..*) --- (1..*) Programme

  constructor(id: string, name: string) {
    this.programID = id;
    this.ProgramName = name;
  }
}

// --------------------------------------------------------------- Faculty
class Faculty {
  readonly facultyID: string;
  readonly facultyName: string;
  schoolName: string = "";
  hod: Faculty | null = null;      // HOD 0..1 (reflexive association)
  readonly courses: Course[] = []; // Teaches: Faculty (1..*) --> Course

  constructor(id: string, name: string) {
    this.facultyID = id;
    this.facultyName = name;
  }

  teaches(c: Course): void { this.courses.push(c); }
}

// ---------------------------------------------------------------- School
class School {
  readonly name: string;
  readonly faculties: Faculty[] = [];  // AssignTo: School (1) <>-- (1..*) Faculty
  readonly programmes: Programme[] = []; // School (1..*) --- (1..*) Programme

  constructor(n: string) {
    this.name = n;
  }

  addFaculty(f: Faculty): void {
    this.faculties.push(f);
    f.schoolName = this.name; // the figure keeps the school name inside Faculty
  }
  removeFaculty(f: Faculty): void {
    unlink(this.faculties, f);
    f.schoolName = "";
  }
  addProgramme(p: Programme): void { // both ends are 1..*, so update both
    this.programmes.push(p);
    p.schools.push(this);
  }
  removeProgramme(p: Programme): void {
    unlink(this.programmes, p);
    unlink(p.schools, this);
  }
}

// --------------------------------------------------------------- Student
class Student implements Addressed {
  readonly stuID: number;
  readonly name: string;
  readonly address: string;
  programme: Programme | null = null; // enrol: Student --> Programme

  constructor(id: number, n: string, addr: string) {
    this.stuID = id;
    this.name = n;
    this.address = addr;
  }

  // Constraint {one student per programme}: a registration binds a student
  // to exactly one programme, so a second enrol() is refused.
  enrol(p: Programme): boolean {
    if (this.programme !== null) {
      console.log(`  refused: ${this.name} is already enrolled in ${this.programme.ProgramName} (one student per programme)`);
      return false;
    }
    this.programme = p;
    return true;
  }
}

// ------------------------------------------------------------ University
class University implements Addressed {
  readonly name: string;
  readonly address: string;
  readonly phone: number;
  readonly schools: School[] = [];   // has: University (1) <>-- (1..*) School
  readonly students: Student[] = []; // registration: University (1) <>-- (*) Student

  constructor(n: string, addr: string, ph: number) {
    this.name = n;
    this.address = addr;
    this.phone = ph;
  }

  addSchool(s: School): void { this.schools.push(s); }
  removeSchool(s: School): void { unlink(this.schools, s); }
  addStudent(s: Student): void { this.students.push(s); }
  removeStudent(s: Student): void { unlink(this.students, s); }
  getSchool(n: string): School | null { return this.schools.find((s) => s.name === n) ?? null; }
  getAllSchool(): School[] { return this.schools; }
  getStudent(id: number): Student | null { return this.students.find((s) => s.stuID === id) ?? null; }
}

// ------------------------------------------------------------------ main
function where(x: Addressed): string { return `${x.name}, ${x.address}`; }

function printUniversity(u: University): void {
  console.log(`${where(u)}, phone ${u.phone}`);
  for (const s of u.getAllSchool()) {
    console.log(`  School: ${s.name}`);
    for (const f of s.faculties) {
      const hod = f.hod ? `, HOD ${f.hod.facultyName}` : "";
      const codes = f.courses.map((c) => ` ${c.courseCode}`).join("");
      console.log(`    Faculty ${f.facultyID} ${f.facultyName} (school ${f.schoolName})${hod} teaches${codes}`);
    }
    for (const p of s.programmes) console.log(`    Programme ${p.programID} ${p.ProgramName}`);
  }
  for (const st of u.students) {
    const prog = st.programme ? st.programme.ProgramName : "not enrolled";
    console.log(`  Student ${st.stuID} ${where(st)} -> ${prog}`);
  }
}

function main(): void {
  const ignou = new University("IGNOU", "Maidan Garhi, New Delhi", 29572514);

  // Two schools
  const socis = new School("SOCIS");
  const soms = new School("SOMS");
  ignou.addSchool(socis);
  ignou.addSchool(soms);

  // Faculties, with a HOD link inside SOCIS
  const f1 = new Faculty("F01", "Dr. Sharma");
  const f2 = new Faculty("F02", "Dr. Verma");
  const f3 = new Faculty("F03", "Dr. Iyer");
  socis.addFaculty(f1);
  socis.addFaculty(f2);
  soms.addFaculty(f3);
  f2.hod = f1; // HOD 0..1

  // Programmes
  const mca = new Programme("MCA", "Master of Computer Applications");
  const mba = new Programme("MBA", "Master of Business Administration");
  socis.addProgramme(mca);
  soms.addProgramme(mba);

  // Courses and the catalogue operations
  const c1 = new Course("MCS-217", "Software Engineering");
  const c2 = new Course("MCSL-222", "OOAD and Web Technologies Lab");
  const c3 = new Course("MMPC-001", "Management Concepts");
  Course.addCourse(c1);
  Course.addCourse(c2);
  Course.addCourse(c3);
  c1.programmes.push(mca); // Course (1) <>-- (*) Programme
  c2.programmes.push(mca);
  c3.programmes.push(mba);
  f1.teaches(c1);
  f2.teaches(c2);
  f3.teaches(c3);

  // Students and registration
  const s1 = new Student(2401, "Asha", "Jaipur");
  const s2 = new Student(2402, "Ravi", "Pune");
  ignou.addStudent(s1);
  ignou.addStudent(s2);
  s1.enrol(mca);
  s2.enrol(mba);

  console.log("--- after setup ---");
  printUniversity(ignou);

  console.log("--- constraint check ---");
  s1.enrol(mba); // refused: already in MCA

  console.log("--- lookups ---");
  // The lookups return `T | null`; `!` tells the checker these hits exist.
  console.log(`getSchool("SOMS") -> ${ignou.getSchool("SOMS")!.name}`);
  // "nullptr" is printed for a miss so the output matches the C++ version.
  console.log(`getSchool("SOL") -> ${ignou.getSchool("SOL") ? "found" : "nullptr"}`);
  console.log(`getStudent(2402) -> ${ignou.getStudent(2402)!.name}`);
  console.log(`getCoursebyCode("MCSL-222") -> ${Course.getCoursebyCode("MCSL-222")!.courseName}`);
  console.log(`getCoursebyName("Management Concepts") -> ${Course.getCoursebyName("Management Concepts")!.courseCode}`);
  console.log(`catalogue size: ${Course.catalogue.length}`);

  console.log("--- removals ---");
  Course.removeCourse(c3);
  socis.removeFaculty(f2);
  soms.removeProgramme(mba);
  ignou.removeStudent(s2);
  ignou.removeSchool(soms);
  console.log(`catalogue size: ${Course.catalogue.length}, f2.schoolName is "${f2.schoolName}", mba.schools.size() = ${mba.schools.length}`);
  printUniversity(ignou);
}

main();

Output

Compiled with zero warnings and run here; this is the real output. All four implementations print exactly this; the outputs were diffed and are identical byte for byte.

--- after setup ---
IGNOU, Maidan Garhi, New Delhi, phone 29572514
  School: SOCIS
    Faculty F01 Dr. Sharma (school SOCIS) teaches MCS-217
    Faculty F02 Dr. Verma (school SOCIS), HOD Dr. Sharma teaches MCSL-222
    Programme MCA Master of Computer Applications
  School: SOMS
    Faculty F03 Dr. Iyer (school SOMS) teaches MMPC-001
    Programme MBA Master of Business Administration
  Student 2401 Asha, Jaipur -> Master of Computer Applications
  Student 2402 Ravi, Pune -> Master of Business Administration
--- constraint check ---
  refused: Asha is already enrolled in Master of Computer Applications (one student per programme)
--- lookups ---
getSchool("SOMS") -> SOMS
getSchool("SOL") -> nullptr
getStudent(2402) -> Ravi
getCoursebyCode("MCSL-222") -> OOAD and Web Technologies Lab
getCoursebyName("Management Concepts") -> MMPC-001
catalogue size: 3
--- removals ---
catalogue size: 2, f2.schoolName is "", mba.schools.size() = 0
IGNOU, Maidan Garhi, New Delhi, phone 29572514
  School: SOCIS
    Faculty F01 Dr. Sharma (school SOCIS) teaches MCS-217
    Programme MCA Master of Computer Applications
  Student 2401 Asha, Jaipur -> Master of Computer Applications

Explanation

Diagram elementWhere it is in the code
Six classesSix class definitions in the same order as the figure’s dependencies: Course, Programme, Faculty, School, Student, University. Forward declarations at the top let each class name the others before they are defined.
AttributesPublic data members with the exact names of the figure, for example std::string facultyID and int phone.
University has 1..* Schoolstd::vector<School*> schools in University; addSchool and removeSchool change it.
University registration * Studentstd::vector<Student*> students; addStudent, removeStudent, getStudent.
Constraint one student per programmeStudent::programme is a single pointer and enrol returns false with a message when it is already set. The output line under constraint check shows the refusal.
School AssignTo 1..* Facultystd::vector<Faculty*> faculties; addFaculty also fills Faculty::schoolName, and removeFaculty clears it, so the attribute in the figure always agrees with the link.
School 1..* to 1..* ProgrammeBoth ends are collections: School::programmes and Programme::schools. addProgramme and removeProgramme update both, which is why mba.schools.size() is 0 after removal.
Faculty HOD 0..1Faculty* hod = nullptr inside Faculty itself: a reflexive association is a pointer to the same class.
Faculty Teaches Coursestd::vector<Course*> courses and teaches() in Faculty only; the arrow is one-way.
Course 1 to * Programmestd::vector<Programme*> programmes in Course, filled in main with push_back.
Course operationsinline static std::vector<Course*> catalogue plus four static functions. getCoursebyName and getCoursebyCode scan the catalogue and return nullptr on a miss.
Remove operationsAll call one helper, unlink, which is std::remove followed by erase.

Diagram element to code, where the four languages differ:

ElementC++RustPythonTypeScript
Object and its linksLocals in main, links are raw pointersEvery object is an Rc<RefCell<T>>; a link is an Rc clone, read with borrow() and changed with borrow_mut()Objects, links are plain referencesObjects, links are typed references; every field, parameter and return carries a type and tsc --strict checks them
1 or 0..1 end (hod, programme)Faculty* hod = nullptrOption<Rc<RefCell<Faculty>>>, None when emptyOptional[Faculty] = Noneunion field hod: Faculty or null, starting as null; a lookup hit in main needs ! before .name because getSchool returns the same union
* or 1..* end (schools, courses)std::vector<School*>Vec<Rc<RefCell<School>>>list[School] with field(default_factory=list)typed array readonly schools: School[] = []; readonly because the array is never replaced, only pushed to
Back end of the two-way School to Programme linkstd::vector<School*> in ProgrammeVec<Weak<RefCell<School>>>; Weak so the two Rc lists do not form a cycle, and addProgramme takes the school’s Rc as this to make itplain listreadonly schools: School[] in Programme
Course catalogue and its four operationsinline static std::vector<Course*> and static member functionsthread_local! static CATALOGUE: RefCell<Vec<..>> and associated functions called as Course::addCourse(&c1)catalogue: ClassVar[list[Course]] and @staticmethodstatic readonly catalogue: Course[] and static methods; the two getters return Course or null
Removing one linkunlink: std::remove then eraseVec::retain with Rc::ptr_eqlist.remove, identity based because every class is @dataclass(eq=False)generic unlink<T>(arr: T[], p: T): indexOf then splice
Attributes shared by two classes (name, address in Student and University)Nothing specialNothing specialNothing specialinterface Addressed implemented by both, so one where() helper prints either
Figure names such as getCoursebyNameAs drawnAs drawn, under #![allow(non_snake_case)]As drawnAs drawn

Question 20

Problem Statement

Write in lab record

Implement the Class Diagram of figure 1.18, in C++ or Java.

Class diagram: Customer places Order, Order contains Product through OrderLine association class

Figure 1.18: Customer Order Association Class

Solution

Write in lab record

Assumptions

A stationery shop records what its customers buy. A customer is identified by name, phone, address, PIN code and e-mail. Each customer places any number of orders, and every order belongs to exactly one customer. An order records the date, who sold it and the total cost. An order contains one or more products; a product has a name, a manufacturer, a product ID, a unit price and the units in stock. The same product can appear in many orders and at a different price each time, and the quantity bought is specific to that order and that product. Those two values, quantity and unit sale price, belong to neither the order nor the product but to the pair, so the figure attaches them to the association as the class OrderLine. The program must create products and a customer, let the customer place orders, add products to an order with a quantity and sale price, reduce stock, refuse a line that asks for more than the stock, and print each order with its lines and total.

  • Types are not shown in the figure. Product_ID and Units_in_Stock are int, prices are double, all other attributes are std::string. OrderDate is a string in YYYY-MM-DD form.
  • OrderLine is its own class with a pointer to its Order, a pointer to its Product, and the two attributes of the figure.
  • places is two-way in the code: Customer::orders holds the 0..* end and Order::customer the 1..1 end. Customer::places sets both.
  • contains is navigable towards Product, so Order holds its OrderLine objects by value in a std::vector and Product holds no back link.
  • ProductOrderCost is derived: contains adds quantity times sale price to it. The figure names the attribute, so it is stored rather than recomputed.
  • The 1..* at Product cannot be checked by the compiler; an order starts empty and the program relies on main adding at least one line.

Diagram elements

ClassAttributesAssociation ends
CustomerC_Name, C_Phone, C_Address, C_Pin, C_Emailplaces: Customer 1..1 to 0..* Order (arrow to Order)
OrderOrderDate, ProductSoldBy, ProductOrderCostcontains: Order 0..* to 1..* Product (arrow to Product)
ProductP_Name, P_Manufacturer, Product_ID, UnitPrice, Units_in_Stocknone
OrderLine (association class)Quantity, UnitSalePriceattached to contains by a dashed line

Steps

  1. Save the listing below as customer_order.cpp in the same session-8 folder.
  2. Compile: clang++ -std=c++17 -Wall -Wextra -o customer_order customer_order.cpp.
  3. Run ./customer_order and paste the output.
  4. For another language, save the matching tab and run it: rustc -O --edition 2021 customer_order.rs && ./customer_order, python3 customer_order.py, or node customer_order.ts.

Program

Lab record: write one language only. Pick yours once and every page opens on it; the other tabs are the same solution for comparison.

customer_order.cppcpp
// customer_order.cpp -- MCSL-222 Session 8, Q20
// Figure 1.18 (Customer places Order, Order contains Product, OrderLine
// association class) implemented in C++17.
// Build: clang++ -std=c++17 -Wall -Wextra -o customer_order customer_order.cpp

#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

class Order;
class Product;

// ------------------------------------------------------------- Product
class Product {
public:
    std::string P_Name;
    std::string P_Manufacturer;
    int Product_ID;
    double UnitPrice;
    int Units_in_Stock;

    Product(std::string name, std::string maker, int id, double price, int stock)
        : P_Name(std::move(name)), P_Manufacturer(std::move(maker)), Product_ID(id),
          UnitPrice(price), Units_in_Stock(stock) {}
};

// ----------------------------------------------------------- OrderLine
// The association class: one object per (Order, Product) pair, holding the
// attributes that belong to the link and not to either end.
class OrderLine {
public:
    Order* order;
    Product* product;
    int Quantity;
    double UnitSalePrice;

    double lineTotal() const { return Quantity * UnitSalePrice; }
};

// --------------------------------------------------------------- Order
class Order {
public:
    std::string OrderDate;
    std::string ProductSoldBy;
    double ProductOrderCost = 0.0;
    class Customer* customer = nullptr;  // back link of "places" (1..1)
    std::vector<OrderLine> lines;        // contains: Order (0..*) --> (1..*) Product

    Order(std::string date, std::string soldBy)
        : OrderDate(std::move(date)), ProductSoldBy(std::move(soldBy)) {}

    // Adds one Product to this Order through an OrderLine.
    bool contains(Product& p, int qty, double salePrice) {
        if (qty <= 0 || qty > p.Units_in_Stock) {
            std::cout << "  refused: only " << p.Units_in_Stock << " x " << p.P_Name
                      << " in stock, asked for " << qty << "\n";
            return false;
        }
        lines.push_back(OrderLine{this, &p, qty, salePrice});
        p.Units_in_Stock -= qty;
        ProductOrderCost += qty * salePrice;
        return true;
    }
};

// ------------------------------------------------------------ Customer
class Customer {
public:
    std::string C_Name;
    std::string C_Phone;
    std::string C_Address;
    std::string C_Pin;
    std::string C_Email;
    std::vector<Order*> orders;  // places: Customer (1..1) --> (0..*) Order

    Customer(std::string name, std::string phone, std::string addr, std::string pin,
             std::string email)
        : C_Name(std::move(name)), C_Phone(std::move(phone)), C_Address(std::move(addr)),
          C_Pin(std::move(pin)), C_Email(std::move(email)) {}

    void places(Order& o) {
        orders.push_back(&o);
        o.customer = this;
    }
};

// ---------------------------------------------------------------- main
static void printOrder(const Order& o) {
    std::cout << "Order dated " << o.OrderDate << " sold by " << o.ProductSoldBy
              << " for " << (o.customer ? o.customer->C_Name : "nobody") << "\n";
    for (const OrderLine& l : o.lines)
        std::cout << "  " << std::left << std::setw(12) << l.product->P_Name
                  << std::right << std::setw(3) << l.Quantity << " x "
                  << std::fixed << std::setprecision(2) << std::setw(9) << l.UnitSalePrice
                  << " = " << std::setw(9) << l.lineTotal() << "\n";
    std::cout << "  ProductOrderCost = " << std::fixed << std::setprecision(2)
              << o.ProductOrderCost << "\n";
}

int main() {
    Product pen("Pen", "Cello", 101, 10.00, 500);
    Product notebook("Notebook", "Classmate", 102, 45.00, 40);
    Product stapler("Stapler", "Kangaro", 103, 120.00, 3);

    Customer asha("Asha", "9876543210", "12 MG Road, Jaipur", "302001", "asha@example.com");

    Order o1("2026-09-01", "Store counter");
    Order o2("2026-09-15", "Online");
    asha.places(o1);
    asha.places(o2);

    o1.contains(pen, 20, 9.50);       // discounted sale price
    o1.contains(notebook, 5, 45.00);
    o2.contains(stapler, 2, 115.00);
    o2.contains(stapler, 2, 115.00);  // refused: only 1 left

    for (const Order* o : asha.orders) printOrder(*o);

    std::cout << "Stock left: pen " << pen.Units_in_Stock << ", notebook "
              << notebook.Units_in_Stock << ", stapler " << stapler.Units_in_Stock << "\n";
    std::cout << asha.C_Name << " has placed " << asha.orders.size() << " orders\n";
    return 0;
}
customer_order.rsrust
// customer_order.rs -- MCSL-222 Session 8, Q20
// Figure 1.18 (Customer places Order, Order contains Product, OrderLine
// association class) in Rust 2021, standard library only.
// Ownership: Rc<RefCell<T>> for Product, Order and Customer; Customer holds Rc links to its orders and Order holds a Weak back link to its customer (1..1), so the two-way "places" link is not an Rc cycle.
// Build: rustc -O --edition 2021 customer_order.rs && ./customer_order
#![allow(non_snake_case)] // attribute names are kept exactly as in the figure
#![allow(dead_code)] // attributes the figure lists (phone, e-mail, manufacturer, ...) that main never reads

use std::cell::RefCell;
use std::rc::{Rc, Weak};

type Ref<T> = Rc<RefCell<T>>;

fn new_ref<T>(x: T) -> Ref<T> {
    Rc::new(RefCell::new(x))
}

// ------------------------------------------------------------- Product
struct Product {
    P_Name: String,
    P_Manufacturer: String,
    Product_ID: i32,
    UnitPrice: f64,
    Units_in_Stock: i32,
}

impl Product {
    fn new(name: &str, maker: &str, id: i32, price: f64, stock: i32) -> Ref<Product> {
        new_ref(Product {
            P_Name: name.into(),
            P_Manufacturer: maker.into(),
            Product_ID: id,
            UnitPrice: price,
            Units_in_Stock: stock,
        })
    }
}

// ----------------------------------------------------------- OrderLine
// The association class: one object per (Order, Product) pair, holding the
// attributes that belong to the link and not to either end.
struct OrderLine {
    order: Weak<RefCell<Order>>,
    product: Ref<Product>,
    Quantity: i32,
    UnitSalePrice: f64,
}

impl OrderLine {
    fn lineTotal(&self) -> f64 {
        f64::from(self.Quantity) * self.UnitSalePrice
    }
}

// --------------------------------------------------------------- Order
struct Order {
    OrderDate: String,
    ProductSoldBy: String,
    ProductOrderCost: f64,
    customer: Option<Weak<RefCell<Customer>>>, // back link of "places" (1..1)
    lines: Vec<OrderLine>,                     // contains: Order (0..*) --> (1..*) Product
}

impl Order {
    fn new(date: &str, sold_by: &str) -> Ref<Order> {
        new_ref(Order {
            OrderDate: date.into(),
            ProductSoldBy: sold_by.into(),
            ProductOrderCost: 0.0,
            customer: None,
            lines: Vec::new(),
        })
    }

    // Adds one Product to this Order through an OrderLine. Takes the order's
    // Rc so the line can keep a back link to it.
    fn contains(this: &Ref<Order>, p: &Ref<Product>, qty: i32, salePrice: f64) -> bool {
        let stock = p.borrow().Units_in_Stock;
        if qty <= 0 || qty > stock {
            println!("  refused: only {} x {} in stock, asked for {}", stock, p.borrow().P_Name, qty);
            return false;
        }
        let mut o = this.borrow_mut();
        o.lines.push(OrderLine {
            order: Rc::downgrade(this),
            product: Rc::clone(p),
            Quantity: qty,
            UnitSalePrice: salePrice,
        });
        p.borrow_mut().Units_in_Stock -= qty;
        o.ProductOrderCost += f64::from(qty) * salePrice;
        true
    }
}

// ------------------------------------------------------------ Customer
struct Customer {
    C_Name: String,
    C_Phone: String,
    C_Address: String,
    C_Pin: String,
    C_Email: String,
    orders: Vec<Ref<Order>>, // places: Customer (1..1) --> (0..*) Order
}

impl Customer {
    fn new(name: &str, phone: &str, addr: &str, pin: &str, email: &str) -> Ref<Customer> {
        new_ref(Customer {
            C_Name: name.into(),
            C_Phone: phone.into(),
            C_Address: addr.into(),
            C_Pin: pin.into(),
            C_Email: email.into(),
            orders: Vec::new(),
        })
    }

    fn places(this: &Ref<Customer>, o: &Ref<Order>) {
        this.borrow_mut().orders.push(Rc::clone(o));
        o.borrow_mut().customer = Some(Rc::downgrade(this));
    }
}

// ---------------------------------------------------------------- main
fn print_order(o: &Order) {
    let who = o
        .customer
        .as_ref()
        .and_then(Weak::upgrade)
        .map(|c| c.borrow().C_Name.clone())
        .unwrap_or_else(|| "nobody".to_string());
    println!("Order dated {} sold by {} for {}", o.OrderDate, o.ProductSoldBy, who);
    for l in &o.lines {
        println!(
            "  {:<12}{:>3} x {:>9.2} = {:>9.2}",
            l.product.borrow().P_Name,
            l.Quantity,
            l.UnitSalePrice,
            l.lineTotal()
        );
    }
    println!("  ProductOrderCost = {:.2}", o.ProductOrderCost);
}

fn main() {
    let pen = Product::new("Pen", "Cello", 101, 10.00, 500);
    let notebook = Product::new("Notebook", "Classmate", 102, 45.00, 40);
    let stapler = Product::new("Stapler", "Kangaro", 103, 120.00, 3);

    let asha = Customer::new("Asha", "9876543210", "12 MG Road, Jaipur", "302001", "asha@example.com");

    let o1 = Order::new("2026-09-01", "Store counter");
    let o2 = Order::new("2026-09-15", "Online");
    Customer::places(&asha, &o1);
    Customer::places(&asha, &o2);

    Order::contains(&o1, &pen, 20, 9.50); // discounted sale price
    Order::contains(&o1, &notebook, 5, 45.00);
    Order::contains(&o2, &stapler, 2, 115.00);
    Order::contains(&o2, &stapler, 2, 115.00); // refused: only 1 left

    for o in &asha.borrow().orders {
        print_order(&o.borrow());
    }

    println!(
        "Stock left: pen {}, notebook {}, stapler {}",
        pen.borrow().Units_in_Stock,
        notebook.borrow().Units_in_Stock,
        stapler.borrow().Units_in_Stock
    );
    println!("{} has placed {} orders", asha.borrow().C_Name, asha.borrow().orders.len());
}
customer_order.pypython
# customer_order.py -- MCSL-222 Session 8, Q20
# Figure 1.18 (Customer places Order, Order contains Product, OrderLine
# association class) in Python 3, standard library only.
# Run: python3 customer_order.py
# eq=False keeps identity comparison, so the two-way Customer/Order links
# cannot recurse through a field-by-field ==.
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional


# ------------------------------------------------------------- Product
@dataclass(eq=False)
class Product:
    P_Name: str
    P_Manufacturer: str
    Product_ID: int
    UnitPrice: float
    Units_in_Stock: int


# ----------------------------------------------------------- OrderLine
# The association class: one object per (Order, Product) pair, holding the
# attributes that belong to the link and not to either end.
@dataclass(eq=False)
class OrderLine:
    order: Order
    product: Product
    Quantity: int
    UnitSalePrice: float

    def lineTotal(self) -> float:
        return self.Quantity * self.UnitSalePrice


# --------------------------------------------------------------- Order
@dataclass(eq=False)
class Order:
    OrderDate: str
    ProductSoldBy: str
    ProductOrderCost: float = 0.0
    customer: Optional[Customer] = None                    # back link of "places" (1..1)
    lines: list[OrderLine] = field(default_factory=list)  # contains: Order (0..*) --> (1..*) Product

    # Adds one Product to this Order through an OrderLine.
    def contains(self, p: Product, qty: int, salePrice: float) -> bool:
        if qty <= 0 or qty > p.Units_in_Stock:
            print(f"  refused: only {p.Units_in_Stock} x {p.P_Name} in stock, asked for {qty}")
            return False
        self.lines.append(OrderLine(self, p, qty, salePrice))
        p.Units_in_Stock -= qty
        self.ProductOrderCost += qty * salePrice
        return True


# ------------------------------------------------------------ Customer
@dataclass(eq=False)
class Customer:
    C_Name: str
    C_Phone: str
    C_Address: str
    C_Pin: str
    C_Email: str
    orders: list[Order] = field(default_factory=list)  # places: Customer (1..1) --> (0..*) Order

    def places(self, o: Order) -> None:
        self.orders.append(o)
        o.customer = self


# ---------------------------------------------------------------- main
def printOrder(o: Order) -> None:
    who = o.customer.C_Name if o.customer else "nobody"
    print(f"Order dated {o.OrderDate} sold by {o.ProductSoldBy} for {who}")
    for l in o.lines:
        print(f"  {l.product.P_Name:<12}{l.Quantity:>3} x {l.UnitSalePrice:>9.2f} = {l.lineTotal():>9.2f}")
    print(f"  ProductOrderCost = {o.ProductOrderCost:.2f}")


def main() -> None:
    pen = Product("Pen", "Cello", 101, 10.00, 500)
    notebook = Product("Notebook", "Classmate", 102, 45.00, 40)
    stapler = Product("Stapler", "Kangaro", 103, 120.00, 3)

    asha = Customer("Asha", "9876543210", "12 MG Road, Jaipur", "302001", "asha@example.com")

    o1 = Order("2026-09-01", "Store counter")
    o2 = Order("2026-09-15", "Online")
    asha.places(o1)
    asha.places(o2)

    o1.contains(pen, 20, 9.50)       # discounted sale price
    o1.contains(notebook, 5, 45.00)
    o2.contains(stapler, 2, 115.00)
    o2.contains(stapler, 2, 115.00)  # refused: only 1 left

    for o in asha.orders:
        printOrder(o)

    print(f"Stock left: pen {pen.Units_in_Stock}, notebook {notebook.Units_in_Stock}, "
          f"stapler {stapler.Units_in_Stock}")
    print(f"{asha.C_Name} has placed {len(asha.orders)} orders")


if __name__ == "__main__":
    main()
customer_order.tsts
// customer_order.ts -- MCSL-222 Session 8, Q20
// Figure 1.18 (Customer places Order, Order contains Product, OrderLine
// association class) in TypeScript, no dependencies.
// Run: node customer_order.ts   (Node 22.18 or later strips the types itself)
"use strict";

// ------------------------------------------------------------- Product
class Product {
  readonly P_Name: string;
  readonly P_Manufacturer: string;
  readonly Product_ID: number;
  readonly UnitPrice: number;
  Units_in_Stock: number;

  constructor(name: string, maker: string, id: number, price: number, stock: number) {
    this.P_Name = name;
    this.P_Manufacturer = maker;
    this.Product_ID = id;
    this.UnitPrice = price;
    this.Units_in_Stock = stock;
  }
}

// ----------------------------------------------------------- OrderLine
// The association class: one object per (Order, Product) pair, holding the
// attributes that belong to the link and not to either end.
class OrderLine {
  readonly order: Order;
  readonly product: Product;
  readonly Quantity: number;
  readonly UnitSalePrice: number;

  constructor(order: Order, product: Product, qty: number, salePrice: number) {
    this.order = order;
    this.product = product;
    this.Quantity = qty;
    this.UnitSalePrice = salePrice;
  }

  lineTotal(): number { return this.Quantity * this.UnitSalePrice; }
}

// --------------------------------------------------------------- Order
class Order {
  readonly OrderDate: string;
  readonly ProductSoldBy: string;
  ProductOrderCost: number = 0.0;
  customer: Customer | null = null;   // back link of "places" (1..1)
  readonly lines: OrderLine[] = [];   // contains: Order (0..*) --> (1..*) Product

  constructor(date: string, soldBy: string) {
    this.OrderDate = date;
    this.ProductSoldBy = soldBy;
  }

  // Adds one Product to this Order through an OrderLine.
  contains(p: Product, qty: number, salePrice: number): boolean {
    if (qty <= 0 || qty > p.Units_in_Stock) {
      console.log(`  refused: only ${p.Units_in_Stock} x ${p.P_Name} in stock, asked for ${qty}`);
      return false;
    }
    this.lines.push(new OrderLine(this, p, qty, salePrice));
    p.Units_in_Stock -= qty;
    this.ProductOrderCost += qty * salePrice;
    return true;
  }
}

// ------------------------------------------------------------ Customer
class Customer {
  readonly C_Name: string;
  readonly C_Phone: string;
  readonly C_Address: string;
  readonly C_Pin: string;
  readonly C_Email: string;
  readonly orders: Order[] = []; // places: Customer (1..1) --> (0..*) Order

  constructor(name: string, phone: string, addr: string, pin: string, email: string) {
    this.C_Name = name;
    this.C_Phone = phone;
    this.C_Address = addr;
    this.C_Pin = pin;
    this.C_Email = email;
  }

  places(o: Order): void {
    this.orders.push(o);
    o.customer = this;
  }
}

// ---------------------------------------------------------------- main
function printOrder(o: Order): void {
  const who = o.customer ? o.customer.C_Name : "nobody";
  console.log(`Order dated ${o.OrderDate} sold by ${o.ProductSoldBy} for ${who}`);
  for (const l of o.lines) {
    console.log(`  ${l.product.P_Name.padEnd(12)}${String(l.Quantity).padStart(3)} x ` +
      `${l.UnitSalePrice.toFixed(2).padStart(9)} = ${l.lineTotal().toFixed(2).padStart(9)}`);
  }
  console.log(`  ProductOrderCost = ${o.ProductOrderCost.toFixed(2)}`);
}

function main(): void {
  const pen = new Product("Pen", "Cello", 101, 10.00, 500);
  const notebook = new Product("Notebook", "Classmate", 102, 45.00, 40);
  const stapler = new Product("Stapler", "Kangaro", 103, 120.00, 3);

  const asha = new Customer("Asha", "9876543210", "12 MG Road, Jaipur", "302001", "asha@example.com");

  const o1 = new Order("2026-09-01", "Store counter");
  const o2 = new Order("2026-09-15", "Online");
  asha.places(o1);
  asha.places(o2);

  o1.contains(pen, 20, 9.50);      // discounted sale price
  o1.contains(notebook, 5, 45.00);
  o2.contains(stapler, 2, 115.00);
  o2.contains(stapler, 2, 115.00); // refused: only 1 left

  for (const o of asha.orders) printOrder(o);

  console.log(`Stock left: pen ${pen.Units_in_Stock}, notebook ${notebook.Units_in_Stock}, stapler ${stapler.Units_in_Stock}`);
  console.log(`${asha.C_Name} has placed ${asha.orders.length} orders`);
}

main();

Output

Compiled with zero warnings and run here; this is the real output. All four implementations print exactly this; the outputs were diffed and are identical byte for byte.

  refused: only 1 x Stapler in stock, asked for 2
Order dated 2026-09-01 sold by Store counter for Asha
  Pen          20 x      9.50 =    190.00
  Notebook      5 x     45.00 =    225.00
  ProductOrderCost = 415.00
Order dated 2026-09-15 sold by Online for Asha
  Stapler       2 x    115.00 =    230.00
  ProductOrderCost = 230.00
Stock left: pen 480, notebook 35, stapler 1
Asha has placed 2 orders

Check by hand: 20 x 9.50 + 5 x 45.00 = 415.00; stock of pens 500 - 20 = 480; the second stapler line asks for 2 when only 3 - 2 = 1 is left, so it is refused.

Explanation

Diagram elementWhere it is in the code
Productclass Product with the five attributes and a constructor; it has no link back to orders because the contains arrow points at it.
OrderLine association classclass OrderLine with Order* order, Product* product, int Quantity, double UnitSalePrice. One object exists per (order, product) pair. lineTotal() is the only extra.
Order contains 1..* Productstd::vector<OrderLine> lines in Order. Order::contains(Product&, qty, price) creates the line, reduces Units_in_Stock and adds to ProductOrderCost.
Customer places 0..* Orderstd::vector<Order*> orders in Customer and Order::customer for the 1..1 end. Customer::places sets both, which is why printOrder can print the customer’s name from the order.
Stock guardThe if at the top of contains refuses qty below 1 or above stock, which produced the first line of the output.
PrintingprintOrder walks lines; std::setw and std::setprecision(2) line up the money columns.

Diagram element to code, where the four languages differ:

ElementC++RustPythonTypeScript
OrderLine’s two linksOrder* order, Product* productWeak<RefCell<Order>> back to the order, Rc<RefCell<Product>> to the productorder: Order, product: Product fields of a @dataclass(eq=False)readonly order: Order and readonly product: Product, set once in the constructor
Order contains 1..* Productstd::vector<OrderLine> by valueVec<OrderLine> by valuelist[OrderLine]typed array readonly lines: OrderLine[] = []
places, two-waystd::vector<Order*> plus Customer* customerVec<Rc<..>> plus Option<Weak<RefCell<Customer>>>; places and contains are associated functions taking this: &Rc<..> because the back link needs the Rclist plus Optional[Customer]readonly orders: Order[] plus the union field customer: Customer or null
Stock and cost updates through a linkwrite through the pointerp.borrow_mut().Units_in_Stock -= qtyattribute assignmentfield assignment; Units_in_Stock and ProductOrderCost are the only fields without readonly, which is exactly the set contains changes
Money columnsstd::fixed, setprecision(2), setw(9){:>9.2}f"{x:>9.2f}"x.toFixed(2).padStart(9)
Attributes the figure lists but main never reads (C_Phone, P_Manufacturer)No warning#![allow(dead_code)], otherwise rustc warns about unread fieldsNo warningNo warning

Viva Questions

Do not copy. Read for understanding and the viva

Q: How does a multiplicity of * differ from 1 in the code? A: A 1 or 0..1 end becomes a single pointer (Faculty* hod); a * or 1..* end becomes a std::vector of pointers (std::vector<School*> schools).

Q: Why is an association class a separate class and not extra attributes on Order? A: Quantity and UnitSalePrice belong to one (order, product) pair. Putting them on Order would allow only one product per order; putting them on Product would give every order the same quantity. A separate OrderLine holds one pair.

Q: Where does aggregation show up in the code? A: Nowhere special: an aggregation is implemented the same way as a plain association, a pointer or vector in the whole. The diamond documents a whole-part meaning; the compiler does not enforce it.

Q: How is the reflexive HOD association implemented? A: As a pointer to the same class, Faculty* hod, set to nullptr for a faculty member with no head.

Q: Why are addCourse and the other Course operations static? A: The figure puts them inside Course, but they act on the set of all courses, not on one course. A static function on a shared catalogue matches that meaning.

Q: How is the constraint one student per programme enforced? A: Student::programme is one pointer, and enrol refuses when it is already set. The program prints the refusal so the evaluator can see it.

Q: Why are there no new or delete calls? A: All objects are locals in main; associations store non-owning pointers. That mirrors the diagram, where a link is a reference, not ownership.

Q: What does a one-way arrow change in the code? A: Only the class at the tail keeps a member for the link. Faculty holds courses; Course has no faculties vector.

Common Mistakes

Do not copy. Read for understanding and the viva
  • Storing a * end as a fixed array or a single pointer; the multiplicity says the number is open, so use std::vector.
  • Updating only one end of a two-way association (for example pushing to School::programmes and forgetting Programme::schools), so removals leave dangling links.
  • Flattening OrderLine into extra fields on Order, which silently limits an order to one product.
  • Renaming the attributes and operations (for example getCourseByCode instead of the figure’s getCoursebyCode); the evaluator compares against the figure.
  • Submitting code that compiles with warnings. Fix every -Wall -Wextra message, especially unused parameters.
  • Leaving main empty. The manual wants a program that runs, so exercise every operation and paste the output.

Session Summary

Write in lab record
  • Question 19: student_registration.cpp, .rs, .py and .ts, six classes of figure 1.15 with every attribute, operation and association end, the enrol constraint enforced, run output attached (identical in all four languages)
  • Question 20: customer_order.cpp, .rs, .py and .ts, Customer, Order, Product and the OrderLine association class of figure 1.18, run output attached (identical in all four languages)
  • Problem description and assumptions for both figures, plus a diagram-element table for each
Navigation

Type to search…

↑↓ navigate↵ selectEsc close