Composition
Objective
- Composition and its practical applications in software development.
- Build a complete example using object-oriented programming with composition.
Exercise 1*
Customer Class
A class can have references to objects of other classes as members. This is called composition and is sometimes referred to as a has-a relationship. For example, a Customer
object needs to record the Address
of the client. The address is not a simple string, it is another object with many fields.
-
Add the following
Customer
andAddress
classes to your project:Customer.javapublic class Customer { private String name; private String phone; private int id; public Customer(String name, String phone, int id) { this.name = name; this.phone = phone; this.id = id; } public String getName() { return name; } public String getPhone() { return phone; } public int getId() { return id; } public void setName(String name) { this.name = name; } public void setPhone(String phone) { this.phone = phone; } public void setId(int id) { this.id = id; } @Override public String toString() { return "Name: " + name + ", Phone: " + phone + ", ID: " + id; } }
Address.javapublic class Address { private String street; private String city; private String state; private String zip; public Address(String street, String city, String state, String zip) { this.street = street; this.city = city; this.state = state; this.zip = zip; } public String getStreet() { return street; } public String getCity() { return city; } public String getState() { return state; } public String getZip() { return zip; } public void setStreet(String street) { this.street = street; } public void setCity(String city) { this.city = city; } public void setState(String state) { this.state = state; } public void setZip(String zip) { this.zip = zip; } @Override public String toString() { return street + ", " + city + ", " + state + " " + zip; } }
-
Modify the class
Customer
by adding new data member calledaddress
of typeAddress
. -
Add a fully parameterized constructor in the
Customer
class that initializes all fields including theaddress
. -
Add a setter and a getter for the
address
data member.
- Modify the
toString()
method by adding theAddress.toString()
to the returned string. - Create two
Customer
objects and set their addresses. - Test your code.
Solution
public class Customer {
private String name;
private String phone;
private int id;
private Address address;
public Customer(String name, String phone, int id) {
this.name = name;
this.phone = phone;
this.id = id;
}
public Customer(String name, String phone, int id, Address address) {
this.name = name;
this.phone = phone;
this.id = id;
this.address = address;
}
public String getName() { return name; }
public String getPhone() { return phone; }
public int getId() { return id; }
public Address getAddress() { return address; }
public void setName(String name) { this.name = name; }
public void setPhone(String phone) { this.phone = phone; }
public void setId(int id) { this.id = id; }
public void setAddress(Address address) { this.address = address; }
@Override
public String toString() {
return "Name: " + name + ", Phone: " + phone + ", ID: " + id +
", Address: " + address;
}
}
public class CustomerTest {
public static void main(String[] args) {
Address address1 = new Address("123 Main St", "New York", "NY", "10001");
Address address2 = new Address("456 Oak Ave", "Boston", "MA", "02115");
Customer customer1 = new Customer("John Doe", "555-1234", 1001);
customer1.setAddress(address1);
Customer customer2 = new Customer("Jane Smith", "555-5678", 1002, address2);
System.out.println(customer1);
System.out.println(customer2);
}
}
Exercise 2
Book Class
-
Add the following
Book
andAuthor
classes to your project:Book.javapublic class Book { private String isbn; private String title; private double price; public Book(String isbn, String title, double price) { this.isbn = isbn; this.title = title; this.price = price; } public String getIsbn() { return isbn; } public String getTitle() { return title; } public double getPrice() { return price; } public void setIsbn(String isbn) { this.isbn = isbn; } public void setTitle(String title) { this.title = title; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return title + " [" + isbn + "]" + " $" + price; } }
Author.javapublic class Author { private int id; private String firstName; private String lastName; public Author(int id, String firstName, String lastName) { this.id = id; this.firstName = firstName; this.lastName = lastName; } public int getId() { return id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public void setId(int id) { this.id = id; } public void setFirstName(String firstName) { this.firstName = firstName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return lastName + ", " + firstName; } }
-
Add an
author
with typeAuthor
as a new data member to theBook
class. -
Add a new
Book
constructor. -
Add a getter and a setter for
author
.
-
Use
Author
’stoString()
method inBook
’stoString()
method. -
Test your code.
-
Add the following
Publisher
class to your project:Publisher.javapublic class Publisher { private int id; private String name; private String email; public Publisher(int id, String name, String email) { this.id = id; this.name = name; this.email = email; } public int getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return name + "<" + email + ">"; } }
-
Add an
address
with typeAddress
as a new data member of thePublisher
class. -
Add a new constructor to the
Publisher
class. -
Define a getter and a setter for
address
. -
Use
Address
’stoString()
method inPublisher
’stoString()
method. -
Test your code.
-
Update the
Book
class by adding a new data memberpublisher
of typePublisher
. Repeat all the steps that you did previously with the other classes. -
Test your code with proper data.
Solution
public class Book {
private String isbn;
private String title;
private double price;
private Author author;
private Publisher publisher;
public Book(String isbn, String title, double price) {
this.isbn = isbn;
this.title = title;
this.price = price;
}
public Book(String isbn, String title, double price, Author author) {
this.isbn = isbn;
this.title = title;
this.price = price;
this.author = author;
}
public Book(String isbn, String title, double price, Author author,
Publisher publisher) {
this.isbn = isbn;
this.title = title;
this.price = price;
this.author = author;
this.publisher = publisher;
}
public String getIsbn() { return isbn; }
public String getTitle() { return title; }
public double getPrice() { return price; }
public Author getAuthor() { return author; }
public Publisher getPublisher() { return publisher; }
public void setIsbn(String isbn) { this.isbn = isbn; }
public void setTitle(String title) { this.title = title; }
public void setPrice(double price) { this.price = price; }
public void setAuthor(Author author) { this.author = author; }
public void setPublisher(Publisher publisher) { this.publisher = publisher; }
@Override
public String toString() {
return title + " [" + isbn + "]" + " $" + price +
(author != null ? " by " + author : "") +
(publisher != null ? ", Publisher: " + publisher : "");
}
}
public class Publisher {
private int id;
private String name;
private String email;
private Address address;
public Publisher(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Publisher(int id, String name, String email, Address address) {
this.id = id;
this.name = name;
this.email = email;
this.address = address;
}
public int getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
public Address getAddress() { return address; }
public void setId(int id) { this.id = id; }
public void setName(String name) { this.name = name; }
public void setEmail(String email) { this.email = email; }
public void setAddress(Address address) { this.address = address; }
@Override
public String toString() {
return name + " <" + email + ">" + (address != null ? "; " + address : "");
}
}
public class BookTest {
public static void main(String[] args) {
Address address = new Address("501 Boylston St.", "Boston", "Massachusetts",
"02116");
Publisher publisher = new Publisher(1, "Addison-Wesley Professional",
"[email protected]", address);
Author author = new Author(1, "Donald", "Knuth");
Book book = new Book("978-0201896831",
"The Art of Computer Programming, Vol. 1: "
+ "Fundamental Algorithms",
62.49, author, publisher);
System.out.println(book);
}
}
Exercise 3
A book generally contains multiple chapters. Each chapter has a name and number of pages. We will make this change to the Book
class.
This time we are not going to add Chapter
as a data member but instead we will add a List
of Chapter
objects.
-
Add the following
Chapter
class to your project:Chapter.javapublic class Chapter { private String title; private int pageCount; public Chapter(String title, int pageCount) { this.title = title; this.pageCount = pageCount; } public String getTitle() { return title; } public int getPageCount() { return pageCount; } public void setTitle(String title) { this.title = title; } public void setPageCount(int pageCount) { this.pageCount = pageCount; } @Override public String toString() { return title + " (" + pageCount + " pages)"; } }
-
Add a
List
ofChapter
s namedchapters
as a new data member. There is no need to create or modify the constructor. -
Add a setter and getter for
chapters
. -
Add two methods:
void addChapter(Chapter c)
andvoid removeChapter(Chapter c)
. Theadd()
andremove()
methods will make the changes directly to the list of chapters. -
Add a two methods:
int getPageCount()
, that returns the total number of pages in all the chapters, andint getChapterCount()
. -
Modify the
toString()
method of theBook
class to display the number of chapters and the total number of pages. -
Test your code with proper data. Create a
Book
object that has two chapters, a publisher who has an address, and an author:Address address = new Address("501 Boylston St.", "Boston", "Massachusetts", "02116"); Publisher publisher = new Publisher(1, "Addison-Wesley Professional", "[email protected]"); Author author = new Author(1, "Donald", "Knuth"); Chapter ch1 = new Chapter("Chapter 1: Basic concepts", 229); Chapter ch2 = new Chapter("Chapter 2: Information Structures", 234); Book book = new Book("978-0201896831", "The Art of Computer Programming, Vol. 1: Fundamental Algorithms", 62.49);
Modify the code above and associated classes so that your output is as follows:
The Art of Computer Programming, Vol. 1: Fundamental Algorithms by Knuth, Donald Publisher: Addison-Wesley Professional <[email protected]m>; 501 Boylston St., Boston, Massachusetts 02116 2 chapters, 463 pages, $62.49, [978-0201896831]
-
To support multiple authors for a single book, change the
author
data member to aList
ofAuthor
s and update the constructor. You also need to add methods likeint getAuthorCount()
,void addAuthor(Author a)
, andvoid removeAuthor(Author a)
.Make sure you do not add duplicate authors for a single book by preventing an author ID from being added twice. Furthermore, you need to print the list of all authors in the
toString()
of theBook
class.
Solution
import java.util.ArrayList;
import java.util.List;
public class Book {
private String isbn;
private String title;
private double price;
private List<Author> authors;
private Publisher publisher;
private List<Chapter> chapters;
public Book(String isbn, String title, double price) {
this.isbn = isbn;
this.title = title;
this.price = price;
this.authors = new ArrayList<Author>();
this.chapters = new ArrayList<Chapter>();
}
public Book(String isbn, String title, double price, Publisher publisher) {
this.isbn = isbn;
this.title = title;
this.price = price;
this.publisher = publisher;
this.authors = new ArrayList<Author>();
this.chapters = new ArrayList<Chapter>();
}
public String getIsbn() { return isbn; }
public String getTitle() { return title; }
public double getPrice() { return price; }
public List<Author> getAuthors() { return authors; }
public Publisher getPublisher() { return publisher; }
public List<Chapter> getChapters() { return chapters; }
public void setIsbn(String isbn) { this.isbn = isbn; }
public void setTitle(String title) { this.title = title; }
public void setPrice(double price) { this.price = price; }
public void setAuthors(List<Author> authors) { this.authors = authors; }
public void setPublisher(Publisher publisher) { this.publisher = publisher; }
public void setChapters(List<Chapter> chapters) { this.chapters = chapters; }
public void addAuthor(Author a) {
for (Author author : authors) {
if (author.getId() == a.getId()) {
return;
}
}
authors.add(a);
}
public void removeAuthor(Author a) { authors.remove(a); }
public int getAuthorCount() { return authors.size(); }
public void addChapter(Chapter c) { chapters.add(c); }
public void removeChapter(Chapter c) { chapters.remove(c); }
public int getPageCount() {
int totalPages = 0;
for (Chapter chapter : chapters) {
totalPages += chapter.getPageCount();
}
return totalPages;
}
public int getChapterCount() { return chapters.size(); }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(title);
if (!authors.isEmpty()) {
sb.append(" by ");
for (int i = 0; i < authors.size(); i++) {
sb.append(authors.get(i));
if (i < authors.size() - 1) {
sb.append(", ");
}
}
}
sb.append("\n");
if (publisher != null) {
sb.append("Publisher: ").append(publisher).append("\n");
}
sb.append(getChapterCount()).append(" chapters, ");
sb.append(getPageCount()).append(" pages, $");
sb.append(String.format("%.2f", price)).append(", [");
sb.append(isbn).append("]");
return sb.toString();
}
}
public class BookTest {
public static void main(String[] args) {
Address address =
new Address("501 Boylston St.", "Boston", "Massachusetts", "02116");
Publisher publisher =
new Publisher(1, "Addison-Wesley Professional", "[email protected]");
publisher.setAddress(address);
Author author = new Author(1, "Donald", "Knuth");
Chapter ch1 = new Chapter("Chapter 1: Basic concepts", 229);
Chapter ch2 = new Chapter("Chapter 2: Information Structures", 234);
Book book = new Book("978-0201896831",
"The Art of Computer Programming, Vol. 1: "
+ "Fundamental Algorithms",
62.49);
book.setPublisher(publisher);
book.addAuthor(author);
book.addChapter(ch1);
book.addChapter(ch2);
System.out.println(book);
}
}
Exercise 4+
Hotel Application
We are going to build a mini-hotel application. The hotel has a name, a location, and a list of rooms. Rooms can be booked and released. Three types of rooms available: single, double, and suite.
-
Add the following
Location
class to your project:Location.javapublic class Location { private String city; private String country; public Location(String city, String country) { this.city = city; this.country = country; } public String getCity() { return city; } public String getCountry() { return country; } public void setCity(String city) { this.city = city; } public void setCountry(String country) { this.country = country; } @Override public String toString() { return city + ", " + country; } }
-
Add the following
Room
class to your project:Room.javapublic class Room { private int number; private int floor; private boolean available; public Room(int number, int floor, boolean available) { this.number = number; this.floor = floor; this.available = available; } public int getNumber() { return number; } public int getFloor() { return floor; } public boolean isAvailable() { return available; } public void setNumber(int number) { this.number = number; } public void setFloor(int floor) { this.floor = floor; } public void setAvailable(boolean available) { this.available = available; } @Override public String toString() { return String.format("Room number: %d\tFloor: %d\tAvailable: %s", number, floor, (this.isAvailable() ? "Yes" : "No")); } }
-
Create a
RoomType
enumeration with three values:SINGLE
,DOUBLE
,SUITE
. -
Make
roomType
:RoomType
a field of theRoom
class. Add it to the fully parameterized constructor. -
Add a setter and getter for the
roomType
data member. -
Modify the
toString()
method of theRoom
class based on the following example:Room room = new Room(425,4, true, RoomType.DOUBLE); System.out.println(room);
[DOUBLE] Room number: 425 Floor: 4 Available: Yes
Composing Classes
The Hotel
class has three fields: name
, location
, and rooms
.
-
Provide two constructors: one takes the
name
, and the other takes thename
andlocation
. -
Provide setters and getters for all three fields.
-
Add three private constants to the
Hotel
class:private final double SINGLE_ROOM_RATE = 355.5; private final double DOUBLE_ROOM_RATE = 515.5; private final double SUITE_RATE = 1079.5;
-
Provide all the following methods:
public double getRoomRate(RoomType type)
: returns the rate for a given room type.public void addRoom(Room r)
: adds a new object to the hotel’s list of rooms.public void reserveRoom(int roomNumber)
: sets the room availability tofalse
.public void releaseRoom(int roomNumber)
: sets the room availability totrue
.public boolean isRoomAvailable(int roomNumber)
: returns room availability.public List<Room> getAvailableRooms()
: returns a list of the available rooms.public int getAvailableRoomsCount()
: returns the number of available rooms in the hotel’s list of rooms.public int getRoomsCount()
: returns the number of all hotel rooms.public List<Room> roomsInFloor(int floorNumber)
: returns a list of the rooms in a specific floor number.
-
Provide a
toString()
method that reuses code from all the included classes and generates a string based on the following output:Hotel: Hilton. Address: Doha, Qatar Room Count: 4 (Available: 3) Today's Income: $515.50 [DOUBLE] Room number: 425 Floor: 4 Available: No @ $515.50 [SINGLE] Room number: 423 Floor: 4 Available: Yes @ $355.50 [DOUBLE] Room number: 328 Floor: 3 Available: Yes @ $515.50 [SUITE] Room number: 218 Floor: 2 Available: Yes @ $1,079.50
Hotel Tester
-
Test your classes using the following code:
Hotel hotel = new Hotel("Hilton"); Room room1 = new Room(425, 4, true, RoomType.DOUBLE); Room room2 = new Room(423, 4, true, RoomType.SINGLE); Room room3 = new Room(328, 3, true, RoomType.DOUBLE); Room room4 = new Room(218, 2, true, RoomType.SUITE); hotel.setLocation(new Location("Doha", "Qatar")); hotel.addRoom(room1); hotel.addRoom(room2); hotel.addRoom(room3); hotel.addRoom(room4); hotel.reserveRoom(425); hotel.reserveRoom(218); System.out.println(hotel);
Booking Application
- Create a class named
BookingApplication
. - The
BookingApplication
class has two fields: a name and a list of hotels. - Discuss the usage of this application:
- What can you do with it?
- How can you benefit from the available code?
- What changes are needed to make it a fully-fledged application?
Solution
public enum RoomType {
SINGLE, DOUBLE, SUITE
}
public class Room {
private int number;
private int floor;
private boolean available;
private RoomType roomType;
public Room(int number, int floor, boolean available, RoomType roomType) {
this.number = number;
this.floor = floor;
this.available = available;
this.roomType = roomType;
}
public int getNumber() { return number; }
public int getFloor() { return floor; }
public boolean isAvailable() { return available; }
public RoomType getRoomType() { return roomType; }
public void setNumber(int number) { this.number = number; }
public void setFloor(int floor) { this.floor = floor; }
public void setAvailable(boolean available) { this.available = available; }
public void setRoomType(RoomType roomType) { this.roomType = roomType; }
@Override
public String toString() {
return String.format("[%-6s] Room number: %d\tFloor: %d\tAvailable: %s",
roomType, number, floor,
(this.isAvailable() ? "Yes" : "No"));
}
}
import java.util.ArrayList;
import java.util.List;
public class Hotel {
private String name;
private Location location;
private List<Room> rooms;
private final double SINGLE_ROOM_RATE = 355.5;
private final double DOUBLE_ROOM_RATE = 515.5;
private final double SUITE_RATE = 1079.5;
public Hotel(String name) {
this.name = name;
this.rooms = new ArrayList<Room>();
}
public Hotel(String name, Location location) {
this.name = name;
this.location = location;
this.rooms = new ArrayList<Room>();
}
public String getName() { return name; }
public Location getLocation() { return location; }
public List<Room> getRooms() { return rooms; }
public void setName(String name) { this.name = name; }
public void setLocation(Location location) { this.location = location; }
public void setRooms(List<Room> rooms) { this.rooms = rooms; }
public double getRoomRate(RoomType type) {
switch (type) {
case SINGLE:
return SINGLE_ROOM_RATE;
case DOUBLE:
return DOUBLE_ROOM_RATE;
case SUITE:
return SUITE_RATE;
default:
return 0.0;
}
}
public void addRoom(Room r) { rooms.add(r); }
public void reserveRoom(int roomNumber) {
for (Room room : rooms) {
if (room.getNumber() == roomNumber) {
room.setAvailable(false);
return;
}
}
}
public void releaseRoom(int roomNumber) {
for (Room room : rooms) {
if (room.getNumber() == roomNumber) {
room.setAvailable(true);
return;
}
}
}
public boolean isRoomAvailable(int roomNumber) {
for (Room room : rooms) {
if (room.getNumber() == roomNumber) {
return room.isAvailable();
}
}
return false;
}
public List<Room> getAvailableRooms() {
List<Room> availableRooms = new ArrayList<Room>();
for (Room room : rooms) {
if (room.isAvailable()) {
availableRooms.add(room);
}
}
return availableRooms;
}
public int getAvailableRoomsCount() { return getAvailableRooms().size(); }
public int getRoomsCount() { return rooms.size(); }
public List<Room> roomsInFloor(int floorNumber) {
List<Room> floorRooms = new ArrayList<Room>();
for (Room room : rooms) {
if (room.getFloor() == floorNumber) {
floorRooms.add(room);
}
}
return floorRooms;
}
public double getTodaysIncome() {
double income = 0.0;
for (Room room : rooms) {
if (!room.isAvailable()) {
income += getRoomRate(room.getRoomType());
}
}
return income;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Hotel: ").append(name).append(". Address: ").append(location)
.append("\n");
sb.append("Room Count: ").append(getRoomsCount());
sb.append(" (Available: ").append(getAvailableRoomsCount()).append(")\n");
sb.append(String.format("Today's Income: $%.2f%n", getTodaysIncome()));
for (Room room : rooms) {
sb.append(room);
sb.append(String.format(" @ $%,.2f%n", getRoomRate(room.getRoomType())));
}
return sb.toString();
}
}
public class HotelTest {
public static void main(String[] args) {
Hotel hotel = new Hotel("Hilton");
Room room1 = new Room(425, 4, true, RoomType.DOUBLE);
Room room2 = new Room(423, 4, true, RoomType.SINGLE);
Room room3 = new Room(328, 3, true, RoomType.DOUBLE);
Room room4 = new Room(218, 2, true, RoomType.SUITE);
hotel.setLocation(new Location("Doha", "Qatar"));
hotel.addRoom(room1);
hotel.addRoom(room2);
hotel.addRoom(room3);
hotel.addRoom(room4);
hotel.reserveRoom(425);
hotel.reserveRoom(218);
System.out.println(hotel);
}
}
import java.util.ArrayList;
import java.util.List;
public class BookingApplication {
private String name;
private List<Hotel> hotels;
public BookingApplication(String name) {
this.name = name;
this.hotels = new ArrayList<Hotel>();
}
public String getName() { return name; }
public List<Hotel> getHotels() { return hotels; }
public void setName(String name) { this.name = name; }
public void setHotels(List<Hotel> hotels) { this.hotels = hotels; }
public void addHotel(Hotel hotel) { hotels.add(hotel); }
public void removeHotel(Hotel hotel) { hotels.remove(hotel); }
public int getHotelCount() { return hotels.size(); }
public List<Room> findAvailableRooms(RoomType type) {
List<Room> availableRooms = new ArrayList<Room>();
for (Hotel hotel : hotels) {
for (Room room : hotel.getAvailableRooms()) {
if (room.getRoomType() == type) {
availableRooms.add(room);
}
}
}
return availableRooms;
}
public List<Hotel> findHotelsByCity(String city) {
List<Hotel> cityHotels = new ArrayList<Hotel>();
for (Hotel hotel : hotels) {
if (hotel.getLocation() != null &&
hotel.getLocation().getCity().equalsIgnoreCase(city)) {
cityHotels.add(hotel);
}
}
return cityHotels;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Booking Application: ").append(name).append("\n");
sb.append("Total Hotels: ").append(getHotelCount()).append("\n\n");
for (Hotel hotel : hotels) {
sb.append(hotel).append("\n");
}
return sb.toString();
}
}
-
What can you do with it?
- Manage multiple hotels in different locations
- Search for available rooms by type across all hotels
- Find hotels in specific cities
- Track room availability and reservations
- Calculate daily income for hotels
-
How can you benefit from the available code?
- Provide a foundation for a hotel booking system
- Demonstrate composition relationships between classes
- Show how to manage collections of objects
- Implement enumeration for room types
- Provide methods for common booking operations
-
What changes are needed to make it a fully-fledged application?
- Add customer/guest management
- Implement date-based reservations (check-in/check-out dates)
- Add payment processing functionality
- Create a user interface (GUI or web-based)
- Add database persistence for data storage
- Implement search and filtering capabilities
- Add booking history and cancellation features
- Include room amenities and descriptions
- Add pricing variations (seasonal rates, discounts)
- Implement authentication and authorization
- Add email notifications for bookings
- Include reviews and ratings system