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
CustomerandAddressclasses 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
Customerby adding new data member calledaddressof typeAddress. -
Add a fully parameterized constructor in the
Customerclass that initializes all fields including theaddress. -
Add a setter and a getter for the
addressdata member.
- Modify the
toString()method by adding theAddress.toString()to the returned string. - Create two
Customerobjects 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
BookandAuthorclasses 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
authorwith typeAuthoras a new data member to theBookclass. -
Add a new
Bookconstructor. -
Add a getter and a setter for
author.
-
Use
Author’stoString()method inBook’stoString()method. -
Test your code.
-
Add the following
Publisherclass 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
addresswith typeAddressas a new data member of thePublisherclass. -
Add a new constructor to the
Publisherclass. -
Define a getter and a setter for
address. -
Use
Address’stoString()method inPublisher’stoString()method. -
Test your code.
-
Update the
Bookclass by adding a new data memberpublisherof 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
Chapterclass 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
ListofChapters namedchaptersas 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 theBookclass to display the number of chapters and the total number of pages. -
Test your code with proper data. Create a
Bookobject 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]>; 501 Boylston St., Boston, Massachusetts 02116 2 chapters, 463 pages, $62.49, [978-0201896831] -
To support multiple authors for a single book, change the
authordata member to aListofAuthors 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 theBookclass.
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
Locationclass 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
Roomclass 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
RoomTypeenumeration with three values:SINGLE,DOUBLE,SUITE. -
Make
roomType:RoomTypea field of theRoomclass. Add it to the fully parameterized constructor. -
Add a setter and getter for the
roomTypedata member. -
Modify the
toString()method of theRoomclass 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 thenameandlocation. -
Provide setters and getters for all three fields.
-
Add three private constants to the
Hotelclass: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
BookingApplicationclass 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