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| Question | Requirement | Status |
|---|---|---|
| Q19 | Implement the following Class Diagram in C++/Java | Complete |
| Q20 | Implement the Class Diagram of figure 1.18, in C++ or Java | Complete |
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:
1becomes a single reference,*becomes aList(Java) orstd::vector(C++). - Write a small
mainthat creates a few objects and exercises every operation so the program actually runs.
Question 19
Problem Statement
Write in lab recordImplement the following Class Diagram in C++/Java.
Figure 1.15: Class Diagram for Student Registration
Solution
Write in lab recordAssumptions
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 andmainare given in Rust, Python and TypeScript, and all four print the same output. - Objects are created in
mainand linked with pointers; no object owns another, so there is nonewordelete. - A
1or0..1end is a pointer; a*or1..*end is astd::vectorof pointers. - The four operations shown inside
Course(addCourse,removeCourse,getCoursebyName,getCoursebyCode) manage the list of all courses, so they arestaticand 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, soCourseholds the vector of programmes. - The
Teachesarrow points from Faculty to Course, so onlyFacultyholds the link. Theenrolarrow points from Student to Programme, so onlyStudentholds the link. - The constraint
one student per programmeon the registration link is read as: one registration binds a student to exactly one programme.Student::enrolrefuses a second programme. getAllSchoolreturns the vector of schools;getSchoolandgetStudentreturnnullptrwhen nothing matches.
Diagram elements
| Class | Attributes | Operations | Association ends |
|---|---|---|---|
| University | name: String, address: String, phone: integer | addSchool, removeSchool, addStudent, removeStudent, getSchool, getAllSchool, getStudent | has 1 to 1..* School (aggregation); registration 1 to * Student (aggregation, constraint one student per programme) |
| School | name: String | addFaculty, removeFaculty, addProgramme, removeProgramme | AssignTo 1 to 1..* Faculty (aggregation); 1..* to 1..* Programme |
| Faculty | facultyID: String, facultyName: String, schoolName: String | none | HOD 0..1 to Faculty (reflexive); Teaches 1..* Faculty to Course (arrow to Course) |
| Programme | programID, ProgramName | none | * Programme to 1 Course (diamond on Course) |
| Course | courseCode, courseName | addCourse, removeCourse, getCoursebyName, getCoursebyCode | see Programme and Faculty |
| Student | stuID: integer, name: String, address: String | none | enrol Student to Programme (arrow to Programme) |
Steps
- Create the folder
session-8and save the listing below asstudent_registration.cpp. - Compile:
clang++ -std=c++17 -Wall -Wextra -o student_registration student_registration.cpp. The build must print nothing. - Run
./student_registrationand paste the output into the record. - For another language, save the matching tab and run it:
rustc -O --edition 2021 student_registration.rs && ./student_registration,python3 student_registration.py, ornode 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.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.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.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.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 ApplicationsExplanation
| Diagram element | Where it is in the code |
|---|---|
| Six classes | Six 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. |
| Attributes | Public data members with the exact names of the figure, for example std::string facultyID and int phone. |
| University has 1..* School | std::vector<School*> schools in University; addSchool and removeSchool change it. |
| University registration * Student | std::vector<Student*> students; addStudent, removeStudent, getStudent. |
| Constraint one student per programme | Student::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..* Faculty | std::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..* Programme | Both 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..1 | Faculty* hod = nullptr inside Faculty itself: a reflexive association is a pointer to the same class. |
| Faculty Teaches Course | std::vector<Course*> courses and teaches() in Faculty only; the arrow is one-way. |
| Course 1 to * Programme | std::vector<Programme*> programmes in Course, filled in main with push_back. |
| Course operations | inline static std::vector<Course*> catalogue plus four static functions. getCoursebyName and getCoursebyCode scan the catalogue and return nullptr on a miss. |
| Remove operations | All call one helper, unlink, which is std::remove followed by erase. |
Diagram element to code, where the four languages differ:
| Element | C++ | Rust | Python | TypeScript |
|---|---|---|---|---|
| Object and its links | Locals in main, links are raw pointers | Every object is an Rc<RefCell<T>>; a link is an Rc clone, read with borrow() and changed with borrow_mut() | Objects, links are plain references | Objects, 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 = nullptr | Option<Rc<RefCell<Faculty>>>, None when empty | Optional[Faculty] = None | union 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 link | std::vector<School*> in Programme | Vec<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 it | plain list | readonly schools: School[] in Programme |
| Course catalogue and its four operations | inline static std::vector<Course*> and static member functions | thread_local! static CATALOGUE: RefCell<Vec<..>> and associated functions called as Course::addCourse(&c1) | catalogue: ClassVar[list[Course]] and @staticmethod | static readonly catalogue: Course[] and static methods; the two getters return Course or null |
| Removing one link | unlink: std::remove then erase | Vec::retain with Rc::ptr_eq | list.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 special | Nothing special | Nothing special | interface Addressed implemented by both, so one where() helper prints either |
Figure names such as getCoursebyName | As drawn | As drawn, under #![allow(non_snake_case)] | As drawn | As drawn |
Question 20
Problem Statement
Write in lab recordImplement the Class Diagram of figure 1.18, in C++ or Java.
Figure 1.18: Customer Order Association Class
Solution
Write in lab recordAssumptions
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_IDandUnits_in_Stockareint, prices aredouble, all other attributes arestd::string.OrderDateis a string inYYYY-MM-DDform. OrderLineis its own class with a pointer to itsOrder, a pointer to itsProduct, and the two attributes of the figure.placesis two-way in the code:Customer::ordersholds the0..*end andOrder::customerthe1..1end.Customer::placessets both.containsis navigable towards Product, soOrderholds itsOrderLineobjects by value in astd::vectorandProductholds no back link.ProductOrderCostis derived:containsaddsquantity times sale priceto 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 onmainadding at least one line.
Diagram elements
| Class | Attributes | Association ends |
|---|---|---|
| Customer | C_Name, C_Phone, C_Address, C_Pin, C_Email | places: Customer 1..1 to 0..* Order (arrow to Order) |
| Order | OrderDate, ProductSoldBy, ProductOrderCost | contains: Order 0..* to 1..* Product (arrow to Product) |
| Product | P_Name, P_Manufacturer, Product_ID, UnitPrice, Units_in_Stock | none |
| OrderLine (association class) | Quantity, UnitSalePrice | attached to contains by a dashed line |
Steps
- Save the listing below as
customer_order.cppin the samesession-8folder. - Compile:
clang++ -std=c++17 -Wall -Wextra -o customer_order customer_order.cpp. - Run
./customer_orderand paste the output. - For another language, save the matching tab and run it:
rustc -O --edition 2021 customer_order.rs && ./customer_order,python3 customer_order.py, ornode 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.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.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, ¬ebook, 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.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.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 ordersCheck 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 element | Where it is in the code |
|---|---|
| Product | class Product with the five attributes and a constructor; it has no link back to orders because the contains arrow points at it. |
| OrderLine association class | class 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..* Product | std::vector<OrderLine> lines in Order. Order::contains(Product&, qty, price) creates the line, reduces Units_in_Stock and adds to ProductOrderCost. |
| Customer places 0..* Order | std::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 guard | The if at the top of contains refuses qty below 1 or above stock, which produced the first line of the output. |
| Printing | printOrder walks lines; std::setw and std::setprecision(2) line up the money columns. |
Diagram element to code, where the four languages differ:
| Element | C++ | Rust | Python | TypeScript |
|---|---|---|---|---|
| OrderLine’s two links | Order* order, Product* product | Weak<RefCell<Order>> back to the order, Rc<RefCell<Product>> to the product | order: Order, product: Product fields of a @dataclass(eq=False) | readonly order: Order and readonly product: Product, set once in the constructor |
| Order contains 1..* Product | std::vector<OrderLine> by value | Vec<OrderLine> by value | list[OrderLine] | typed array readonly lines: OrderLine[] = [] |
| places, two-way | std::vector<Order*> plus Customer* customer | Vec<Rc<..>> plus Option<Weak<RefCell<Customer>>>; places and contains are associated functions taking this: &Rc<..> because the back link needs the Rc | list plus Optional[Customer] | readonly orders: Order[] plus the union field customer: Customer or null |
| Stock and cost updates through a link | write through the pointer | p.borrow_mut().Units_in_Stock -= qty | attribute assignment | field assignment; Units_in_Stock and ProductOrderCost are the only fields without readonly, which is exactly the set contains changes |
| Money columns | std::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 fields | No warning | No warning |
Viva Questions
Do not copy. Read for understanding and the vivaQ: 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 usestd::vector. - Updating only one end of a two-way association (for example pushing to
School::programmesand forgettingProgramme::schools), so removals leave dangling links. - Flattening
OrderLineinto extra fields onOrder, which silently limits an order to one product. - Renaming the attributes and operations (for example
getCourseByCodeinstead of the figure’sgetCoursebyCode); the evaluator compares against the figure. - Submitting code that compiles with warnings. Fix every
-Wall -Wextramessage, especially unused parameters. - Leaving
mainempty. 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,.pyand.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,.pyand.ts, Customer, Order, Product and theOrderLineassociation 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