Aggregation and Composition
EECS2030 B: Advanced Object Oriented Programming Fall 2018
CHEN-WEI WANG
Call by Value (2.1)
For illustration, let’s assume the following variant of the Point class:
class Point { int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y; }
void moveVertically(int y){ this.y += y;
}
void moveHorizontally(int x){
this.x += x; }
}
3 of 33
Call by Value (1)
● Consider the general form of a call to some mutator method m1, with context object s and argument value arg:
○ To execute , an implicit is done.
⇒ A of value stored in arg is passed for the method call.
● What can the type T be? [ Primitive or Reference ] ○ is primitive type (e.g., int, char, boolean, etc.):
: Copy of arg’s value (e.g., 2, ‘j’) is passed. ○ is reference type (e.g., String, Point, Person, etc.):
: Copy of arg’s stored reference/address 2 of 33 (e.g., Point@5cb0d902) is passed.
class Supplier { void m1( T par) {
/* manipulate par */
} }
class Client {
Supplier s = new Supplier();
T arg = …; s.m1(arg)
}
s.m1(arg)
par := arg
copy
T
Call by Value
T
Call by Value
Call by Value (2.2.1)
1 2 3 4 5 6 7 8
● Before the mutator call at L6, primitive variable i stores 10.
public class Util {
void reassignInt(int j) {
j = j + 1; }
void reassignRef(Point q) {
Point np = new Point(6, 8);
q = np; }
void changeViaRef(Point q) {
q.moveHorizontally(3); q.moveVertically(4); } }
@Test
public void testCallByVal() {
Util u = new Util(); int i = 10; assertTrue(i == 10); u.reassignInt(i); assertTrue(i == 10);
}
● When executing the mutator call at L6, due to call by value , a copy of variable i is made.
⇒ The assignment i = i + 1 is only effective on this copy, not the original variable i itself.
● ∴ After the mutator call at L6, variable i still stores 10. 4 of 33
Call by Value (2.2.2)
Before reassignInt During reassignInt After reassignInt
i j
int
i j
int
10
i
int
10
5 of 33
int
10
10
int
11
Call by Value (2.3.2)
Before reassignRef During reassignRef After reassignRef
7 of 33
Point x3
py4 q
Point x3
py4
Point x3
py4
q
Point x6 y8
Call by Value (2.3.1)
1 2 3 4 5 6 7 8 9
public class Util {
void reassignInt(int j) {
j = j + 1; }
void reassignRef(Point q) {
Point np = new Point(6, 8);
q = np; }
void changeViaRef(Point q) {
q.moveHorizontally(3); q.moveVertically(4); } }
@Test
public void testCallByRef_1() {
Util u = new Util();
Point p = new Point(3, 4); Point refOfPBefore = p; u.reassignRef(p); assertTrue(p==refOfPBefore); assertTrue(p.x==3 && p.y==4);
}
● Before the mutator call at L6, variable p stores the address of some Point object (whose x is 3 and y is 4).
reference
● When executing the mutator call at L6, due to call by value , a
copy of address stored in p is made.
⇒ The assignment p = np is only effective on this copy, not the original variable p itself.
● ∴ After the mutator call at L6, variable p still stores the original
address (i.e., same as refOfPBefore). 6 of 33
Call by Value (2.4.1)
1 2 3 4 5 6 7 8 9
public class Util {
void reassignInt(int j) {
j = j + 1; }
void reassignRef(Point q) {
Point np = new Point(6, 8);
q = np; }
void changeViaRef(Point q) {
q.moveHorizontally(3); q.moveVertically(4); } }
@Test
public void testCallByRef_2() {
Util u = new Util();
Point p = new Point(3, 4); Point refOfPBefore = p; u.changeViaRef(p); assertTrue(p==refOfPBefore); assertTrue(p.x==6 && p.y==8);
}
● Before the mutator call at L6, reference variable p stores the address of some Point object (whose x is 3 and y is 4).
● When executing the mutator call at L6, due to call by value , a
8 of 33
copy of address stored in p is made. [Alias: p and q store same address.]
⇒ Calls to q.moveHorizontally and q.moveVertically are
effective on both p and q.
● ∴ After the mutator call at L6, variable p still stores the original address (i.e., same as refOfPBefore), but its x and y have been modified via q.
Call by Value (2.4.2)
Before changeViaRef During changeViaRef After changeViaRef
9 of 33
Point x3
py4 q
Point x6
py8 q
Point x3
py4
Aggregation: Independent Containees Shared by Containers (1.1)
Course
class Course {
String title;
Faculty prof; Course(String title) {
this.title = title; }
void setProf(Faculty prof) { this.prof = prof;
}
Faculty getProf() {
return this.prof; }
}
prof 1
Faculty
class Faculty {
String name; Faculty(String name) {
this.name = name; }
void setName(String name) { this.name = name;
}
String getName() {
return this.name; }
}
11 of 33
Aggregation vs. Composition: Terminology
Container object: an object that contains others.
Containee object: an object that is contained within another. ● e.g., Each course has a faculty member as its instructor.
○ Container: Course Containee: Faculty. ● e.g., Each student is registered in a list of courses; Each faculty
member teaches a list of courses.
○ Container: Student, Faculty Containees: Course.
e.g., eecs2030 taken by jim (student) and taught by tom (faculty). ⇒ Containees may be shared by different instances of containers. e.g., When EECS2030 is finished, jim and jackie still exist!
⇒ Containees may exist independently without their containers.
● e.g., In a file system, each directory contains a list of files.
○ Container: Directory Containees: File.
10 of 33
e.g., Each file has exactly one parent directory.
⇒ A containee may be owned by only one container. e.g., Deleting a directory also deletes the files it contains. ⇒ Containees may co-exist with their containers.
Aggregation: Independent Containees Shared by Containers (1.2)
@Test
public void testAggregation1() {
Course eecs2030 = new Course(“Advanced OOP”); Course eecs3311 = new Course(“Software Design”); Faculty prof = new Faculty(“Jackie”); eecs2030.setProf(prof);
eecs3311.setProf(prof);
assertTrue(eecs2030.getProf() == eecs3311.getProf());
/* aliasing */
prof.setName(“Jeff”);
assertTrue(eecs2030.getProf() == eecs3311.getProf()); assertTrue(eecs2030.getProf().getName().equals(“Jeff”));
Faculty prof2 = new Faculty(“Jonathan”); eecs3311.setProf(prof2);
assertTrue(eecs2030.getProf() != eecs3311.getProf()); assertTrue(eecs2030.getProf().getName().equals(“Jeff”)); assertTrue(eecs3311.getProf().getName().equals(“Jonathan”));
}
12 of 33
Aggregation: Independent Containees Shared by Containers (2.1)
Student
cs
Course
te
Faculty
*
*
class Student {
String id; ArrayList
ArrayList
}
class Course { String title; }
class Faculty {
String name; ArrayList
Faculty(String name) { this.name = name; te = new ArrayList<>(); } void addTeaching(Course c) { te.add(c); }
ArrayList
}
13 of 33
The Dot Notation (3.1)
In real life, the relationships among classes are sophisticated.
Student
cs
Course
te
Faculty
*
*
class Student {
String id; ArrayList
}
class Course { String title; Faculty prof;
}
class Faculty {
String name; ArrayList
}
Aggregation links between classes constrain how you can navigate among these classes.
e.g., In the context of class Student:
○ Writing cs denotes the list of registered courses.
○ Writing cs[i] (where i is a valid index) navigates to the class
Course, which changes the context to class Course. 15 of 33
Aggregation: Independent Containees Shared by Containers (2.2)
@Test
public void testAggregation2() {
Faculty p = new Faculty(“Jackie”);
Student s = new Student(“Jim”);
Course eecs2030 = new Course(“Advanced OOP”); Course eecs3311 = new Course(“Software Design”); eecs2030.setProf(p);
eecs3311.setProf(p);
p.addTeaching(eecs2030); p.addTeaching(eecs3311);
s.addCourse(eecs2030);
s.addCourse(eecs3311);
assertTrue(eecs2030.getProf() == s.getCS().get(0).getProf()); assertTrue(s.getCS().get(0).getProf() == s.getCS().get(1).getProf()); assertTrue(eecs3311 == s.getCS().get(1));
assertTrue(s.getCS().get(1) == p.getTE().get(1));
}
14 of 33
The Dot Notation (3.2)
class Student {
String id; ArrayList
}
class Student {
… /* attributes */
/* Get the student’s id */
String getID() { return this.id; }
/* Get the title of the ith course */ String getCourseTitle(int i) {
return this.cs.get(i).title; }
/* Get the instructor’s name of the ith course */
String getInstructorName(int i) { return this.cs.get(i).prof.name;
} }
16 of 33
class Course { String title; Faculty prof;
}
class Faculty {
String name; ArrayList
}
The Dot Notation (3.3)
class Student {
String id; ArrayList
}
class Course {
… /* attributes */
/* Get the course’s title */
String getTitle() { return this.title; } /* Get the instructor’s name */
String getInstructorName() {
return this.prof.name; }
/* Get title of ith teaching course of the instructor */
String getCourseTitleOfInstructor(int i) { return this.prof.te.get(i).title;
} }
17 of 33
class Course { String title; Faculty prof;
}
class Faculty {
String name; ArrayList
}
Composition: Dependent Containees Owned by Containers (1.1)
Assumption: Files are not shared among directories.
Directory
parent files 1*
File
class File {
String name; File(String name) {
this.name = name; }
}
19 of 33
class Directory {
String name;
File[] files;
int nof; /* num of files */ Directory(String name) {
this.name = name;
files = new File[100]; }
void addFile(String fileName) { files[nof] = new File(fileName); nof ++;
} }
The Dot Notation (3.4)
class Student {
String id; ArrayList
}
class Faculty {
… /* attributes */
/* Get the instructor’s name */ String getName() {
return this.name; }
/* Get the title of ith teaching course */
String getCourseTitle(int i) { return this.te.get(i).title;
} }
18 of 33
class Course { String title; Faculty prof;
}
class Faculty {
String name; ArrayList
}
Composition: Dependent Containees Owned by Containers (1.2.1)
1 @Test
2 public void testComposition() {
3
4
5
6
7
8 9}
Directory d1 = new Directory(“D”); d1.addFile(“f1.txt”); d1.addFile(“f2.txt”); d1.addFile(“f3.txt”);
assertTrue( d1.files[0].name.equals(“f1.txt”));
● L4: 1st File object is created and owned exclusively by d1. No other directories are sharing this File object with d1.
● L5: 2nd File object is created and owned exclusively by d1. No other directories are sharing this File object with d1.
● L6: 3rd File object is created and owned exclusively by d1.
No other directories are sharing this File object with d1. 20 of 33
Composition: Dependent Containees Owned by Containers (1.2.2)
Right before test method testComposition terminates:
Directo
files
ry
name
nof
d1 3 d1.files[0]
“f1.txt”
“D” d1.files
d1.files[1]
nof
0 1 2 3 4 5 6 7 … 99 null null null null null null null
d1.files[2]
21 of 33
File
File
File
name
name
name
“f2.txt” “f3.txt”
Composition: Dependent Containees Owned by Containers (1.4.1)
Version 1: by copying all attributes using =.
Is a shallow copy satisfactory to support composition?
i.e., Does it still forbid sharing to occur? [ NO ]
@Test
void testShallowCopyConstructor() {
Directory d1 = new Directory(“D”);
d1.addFile(“f1.txt”); d1.addFile(“f2.txt”); d1.addFile(“f3.txt”); Directory d2 = new Directory(d1);
assertTrue(d1.files == d2.files); /* violation of composition */ d2.files[0].changeName(“f11.txt”); assertFalse(d1.files[0].name.equals(“f1.txt”)); }
23 of 33
Shallow Copy
class Directory { Directory(Directory other) {
/* value copying for primitive type */
nof = other.nof;
/* address copying for reference type */ name = other.name; files = other.files; } }
Composition: Dependent Containees Owned by Containers (1.3)
Problem: Implement a copy constructor for Directory.
A copy constructor is a constructor which initializes attributes
from the argument object other.
Hints:
● The implementation should be consistent with the effect of
copying and pasting a directory.
● Separate copies of files are created. 22 of 33
class Directory { Directory(Directory other) {
/* Initialize attributes via attributes of ‘other’. */
} }
Composition: Dependent Containees Owned by Containers (1.4.2)
Right before test method testShallowCopyConstructor terminates:
Directo name
files
nof
24 of 33
Directo
ry
d2
3
d2.name
“D”
d1.files
d1.files[1]
d2.files[1]
d2.files
ry
nof
name
0 1 2 3 4 5 6 7 … 99
files
null null
null null
null null null
nof
d1 3 d1.files[0]
d2.files[0]
“f11.txt”
d1.files[2]
d2.files[2]
File
File
name
name
name
File
“f2.txt”
“f3.txt”
Composition: Dependent Containees Owned by Containers (1.5.1)
Version 2: a
class File { File(File other) {
this.name =
new String(other.name);
} }
@Test
void testDeepCopyConstructor() {
Directory d1 = new Directory(“D”);
d1.addFile(“f1.txt”); d1.addFile(“f2.txt”); d1.addFile(“f3.txt”); Directory d2 = new Directory(d1);
assertTrue(d1.files != d2.files); /* composition preserved */ d2.files[0].changeName(“f11.txt”);
assertTrue(d1.files[0].name.equals(“f1.txt”)); } 25 of 33
Deep Copy
class Directory { Directory(String name) {
this.name = new String(name);
files = new File[100]; } Directory(Directory other) {
this (other.name);
for(int i = 0; i < nof; i ++) {
File src = other.files[i]; File nf = new File(src); this.addFile(nf); } }
void addFile(File f) { ... } }
Composition: Dependent Containees Owned by Containers (1.5.3)
Q: Composition Violated?
class File { File(File other) {
this.name =
new String(other.name);
} }
@Test
void testDeepCopyConstructor() {
Directory d1 = new Directory("D");
d1.addFile("f1.txt"); d1.addFile("f2.txt"); d1.addFile("f3.txt"); Directory d2 = new Directory(d1);
assertTrue(d1.files != d2.files); /* composition preserved */ d2.files[0].changeName("f11.txt");
assertTrue(d1.files[0] == d2.files[0]); /* composition violated! */
27 of 33
class Directory { Directory(String name) {
this.name = new String(name);
files = new File[100]; } Directory(Directory other) {
this (other.name);
for(int i = 0; i < nof; i ++) {
File src = other.files[i];
this.addFile(src); } } void addFile(File f) { ... } }
}
Composition: Dependent Containees
Owned by Containers (1.5.2)
Right before test method testDeepCopyConstructor terminates:
26 of 33
Directory name
files
d2 nof 3
d2.files[0]
File
“f11.txt” “D”
null null null
null null null null
“D”
d2.name
d2.files
d2.files[1]
File
nof
0 1 2 3 4 5 6 7 ... 99
“f2.txt”
“f3.txt”
nof
d2.files[2]
File
name
name
name
Directory name
files
d1 nof 3
d1.files[0]
File name
d1.files
d1.files[1]
0 1 2 3 4 5 6 7 ... 99
null null null
null null null null
name
File
d1.files[2]
name
File
“f1.txt”
“f2.txt”
“f3.txt”
Composition: Dependent Containees Owned by Containers (1.6)
Exercise: Implement the accessor in class Directory
class Directory { File[] files;
int nof;
File[] getFiles() {
/* Your Task */
} }
so that it preserves composition, i.e., does not allow references of files to be shared.
28 of 33
Aggregation vs. Composition (1)
Terminology:
○ Container object: an object that contains others.
○ Containee object: an object that is contained within another.
Aggregation :
○ Containees (e.g., Course) may be shared among containers (e.g., Student, Faculty).
○ Containees exist independently without their containers. ○ When a container is destroyed, its containees still exist.
Composition :
○ Containers (e.g, Directory, Department) own exclusive access to their containees (e.g., File, Faculty).
29 of 33
○ Containees cannot exist without their containers.
○ Destroying a container destroys its containeees cascadingly.
Index (1)
Call by Value (1) Call by Value (2.1) Call by Value (2.2.1) Call by Value (2.2.2) Call by Value (2.3.1) Call by Value (2.3.2) Call by Value (2.4.1) Call by Value (2.4.2)
Aggregation vs. Composition: Terminology Aggregation: Independent Containees Shared by Containers (1.1)
Aggregation: Independent Containees Shared by Containers (1.2)
Aggregation: Independent Containees
Shared by Containers (2.1)
31 of 33
Aggregation vs. Composition (2)
Aggregations and Compositions may exist at the same time! e.g., Consider a workstation:
○ Each workstation owns CPU, monitor, keyword. [ compositions ] ○ All workstations share the same network. [ aggregations ]
30 of 33
Index (2)
Aggregation: Independent Containees Shared by Containers (2.2)
The Dot Notation (3.1) The Dot Notation (3.2) The Dot Notation (3.3)
The Dot Notation (3.4)
Composition: Dependent Containees Owned by Containers (1.1) Composition: Dependent Containees Owned by Containers (1.2.1) Composition: Dependent Containees Owned by Containers (1.2.2) Composition: Dependent Containees
Owned by Containers (1.3)
32 of 33
Index (3)
Composition: Dependent Containees Owned by Containers (1.4.1) Composition: Dependent Containees Owned by Containers (1.4.2) Composition: Dependent Containees Owned by Containers (1.5.1) Composition: Dependent Containees Owned by Containers (1.5.2) Composition: Dependent Containees Owned by Containers (1.5.3) Composition: Dependent Containees Owned by Containers (1.6)
Aggregation vs. Composition (1)
Aggregation vs. Composition (2)
33 of 33