Wrapping it All Up

The code examples for the Store, Manager, and Address classes illustrate a concept of nested aggregation in object-oriented programming,

Address Class

  • Purpose: Represents a physical address, capturing details such as street address, city, state, and ZIP code.

  • Usage: Used by the Manager and Store classes to represent physical locations, showcasing its reusability and flexibility in modeling real-world entities with an address.

Manager Class

  • Purpose: Represents a manager with personal attributes (full name, age, salary) and a home address.

  • Aggregation: Contains an Address object, demonstrating a "has-a" relationship where a manager has an address. This showcases a single-level aggregation.

Store Class

  • Purpose: Represents a store, including its name, physical address, and the manager in charge.

  • Nested Aggregation:

    • Contains an Address object for the store's location, directly showing a "has-a" relationship.

    • Contains a Manager object, which itself contains an Address object. This represents nested aggregation, where the store not only has a manager but also indirectly contains an address through the manager, illustrating multi-level "has-a" relationships.

Conceptual Summary

  • Nested Aggregation: The overall structure demonstrates nested aggregation, a powerful concept in object-oriented design that allows for the modeling of complex real-world relationships within software. A Store aggregates a Manager, which in turn aggregates an Address, forming a chain of relationships that mirror real-life organizational structures.

  • Object Reusability and Encapsulation: The use of the Address class within both the Manager and Store classes showcases object reusability, where a single class is used to represent similar concepts across different contexts, enhancing code maintainability and consistency. Encapsulation is maintained as each class manages its own state and exposes functionality through methods, ensuring that internal representations are hidden from outside classes.

  • Deep Copying in Constructors: The practice of creating new instances of Address and Manager objects within constructors (rather than sharing references) is a deliberate choice to avoid unintended side effects from shared references. This ensures that each Store and Manager has its unique Address, and changes to one object's address don't inadvertently affect another object.

The Code:

image-20240219-150207.png

 

Address Class

package NestedAggregation; /** * The Address class represents an address with properties for street address, city, state, and ZIP code. * It includes methods for setting and getting these properties, as well as constructors for creating Address objects. */ public class Address { // Attributes of the Address class representing the details of an address. private String streetAddress; private String city; private String state; private String zip; // Constructors /** * Constructor for creating an Address object with all properties specified. * @param streetAddress The street address part of the address. * @param city The city part of the address. * @param state The state part of the address. * @param zip The ZIP code part of the address. */ public Address(String streetAddress, String city, String state, String zip) { this.streetAddress = streetAddress; this.city = city; this.state = state; this.zip = zip; } /** * Default constructor that initializes an Address with unknown properties. */ public Address() { this.streetAddress = "Unknown Street Address"; this.city = "Unknown City"; this.state = "Unknown State"; this.zip = "Unknown Zip"; } /** * Copy constructor for creating a new Address object as a copy of an existing Address object. * This is useful for avoiding reference sharing of Address objects. * @param pAdd The Address object to copy. */ public Address(Address pAdd) { this.streetAddress = pAdd.streetAddress; this.city = pAdd.city; this.state = pAdd.state; this.zip = pAdd.zip; } // Getters and Setters // Provides access to the street address public String getStreetAddress() { return streetAddress; } public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } // Provides access to the city public String getCity() { return city; } public void setCity(String city) { this.city = city; } // Provides access to the state public String getState() { return state; } public void setState(String state) { this.state = state; } // Provides access to the ZIP code public String getZip() { return zip; } public void setZip(String zip) { this.zip = zip; } /** * Overrides the toString method to provide a string representation of the Address object. * @return A string representation of the Address, formatted with street address, city, state, and ZIP code. */ @Override public String toString() { return "\t" + getStreetAddress() + "\n\t" + getCity() + ", " + getState() + ", " + getZip(); } // Override equals method @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Address address = (Address) obj; if (!streetAddress.equals(address.streetAddress)) return false; if (!city.equals(address.city)) return false; if (!state.equals(address.state)) return false; return zip.equals(address.zip); } // Override hashCode method @Override public int hashCode() { int result = streetAddress.hashCode(); result = 31 * result + city.hashCode(); result = 31 * result + state.hashCode(); result = 31 * result + zip.hashCode(); return result; } }

Manager Class

package NestedAggregation; import java.text.NumberFormat; /** * Represents a Manager with personal information and a home address. * Demonstrates an example of aggregation, where Manager contains an Address object. */ public class Manager { // Attributes of the Manager class private String fullName; // Manager's full name private int age; // Manager's age private double salary; // Manager's salary private Address homeAddress; // Aggregation: Manager HAS-A Address // Constructors /** * Parameterized constructor to initialize a Manager object with specified values for * fullName, age, salary, and homeAddress. The homeAddress is deep-copied to ensure the Manager has * a separate instance of Address. * * @param fullName The full name of the manager. * @param age The age of the manager. * @param salary The salary of the manager. * @param homeAddress The home address of the manager, represented by an Address object. */ public Manager(String fullName, int age, double salary, Address homeAddress) { this.fullName = fullName; this.age = age; this.salary = salary; this.homeAddress = new Address(homeAddress); } /** * Default constructor initializes a Manager with default values, including an 'unknown' address. */ public Manager() { this.fullName = "No Name"; this.age = -1; this.salary = 0.00; this.homeAddress = new Address(); // Uses Address's default constructor } /** * Copy constructor creates a new Manager object as a copy of an existing Manager object, * including a deep copy of the homeAddress to ensure independence of Address instances. * * @param pManager The Manager object to copy. */ public Manager(Manager pManager) { this.fullName = pManager.fullName; this.age = pManager.age; this.salary = pManager.salary; this.homeAddress = new Address(pManager.getHomeAddress()); } // Getters and Setters public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public Address getHomeAddress() { return homeAddress; } /** * Sets the home address of the manager. Accepts an Address object and creates a deep copy * to maintain independence from the passed Address instance. * * @param homeAddress The new home address to set for the manager. */ public void setHomeAddress(Address homeAddress) { this.homeAddress = new Address(homeAddress); } /** * Provides a string representation of the Manager object, including all attributes and * the formatted address from the Address object's toString method. * * @return A string representation of the Manager's details and home address. */ @Override public String toString() { NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(); return "\tFullName: " + fullName + "\n" + "\tAge: " + age + "\n" + "\tSalary: " + currencyFormatter.format(salary) + "\n" + "Home Address:\n " + homeAddress + "\n"; } @Override public boolean equals(Object obj) { // Check for reference equality if (this == obj) return true; // Check for null and ensure exact object type match if (obj == null || getClass() != obj.getClass()) return false; Manager manager = (Manager) obj; // Check each field for equality if (age != manager.age) return false; if (Double.compare(manager.salary, salary) != 0) return false; if (fullName != null ? !fullName.equals(manager.fullName) : manager.fullName != null) return false; return homeAddress != null ? homeAddress.equals(manager.homeAddress) : manager.homeAddress == null; } @Override public int hashCode() { int result; long temp; result = fullName != null ? fullName.hashCode() : 0; result = 31 * result + age; temp = Double.doubleToLongBits(salary); result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + (homeAddress != null ? homeAddress.hashCode() : 0); return result; } }

Store Class:

package NestedAggregation; /** * Represents a store with a name, address, and a manager. * Demonstrates nested aggregation by incorporating Address and Manager objects, * where Manager itself includes an Address object, showing multi-level object composition. */ public class Store { // Attributes of the Store class private String storeName; // Name of the store private Address storeAddress; // Address of the store, using the Address class for details private Manager storeManager; // Manager of the store, represented by a Manager object // Constructors /** * Parameterized constructor to initialize a Store object with a name, address, and manager. * Both the address and manager are deep-copied to ensure the Store has separate instances. * * @param storeName Name of the store. * @param storeAddress Address of the store, provided as an Address object. * @param storeManager Manager of the store, provided as a Manager object. */ public Store(String storeName, Address storeAddress, Manager storeManager) { this.storeName = storeName; this.storeAddress = new Address(storeAddress); this.storeManager = new Manager(storeManager); } /** * Default constructor initializes a Store with default values, * including default 'unknown' address and manager objects. */ public Store() { this.storeName = "No Name"; this.storeAddress = new Address(); this.storeManager = new Manager(); } // Getters and Setters public String getStoreName() { return storeName; } public void setStoreName(String storeName) { this.storeName = storeName; } public Address getStoreAddress() { return storeAddress; } /** * Sets the address of the store. Accepts an Address object and creates a deep copy * to maintain independence from the passed Address instance. * * @param storeAddress The new address to set for the store. */ public void setStoreAddress(Address storeAddress) { this.storeAddress = new Address(storeAddress); } public Manager getStoreManager() { return storeManager; } /** * Sets the manager of the store. Accepts a Manager object and creates a deep copy * to ensure the store has its own independent Manager instance. * * @param storeManager The new manager to set for the store. */ public void setStoreManager(Manager storeManager) { this.storeManager = new Manager(storeManager); } /** * Provides a string representation of the Store object, including the store's name, * address (formatted by Address's toString method), and manager (formatted by Manager's toString method). * * @return A string representation of the store's details, including name, address, and manager. */ @Override public String toString() { return "Store: " + "\n" + "\tStore Name: " + storeName + "\n" + "Store Address: \n" + storeAddress + "\n" + "Store Manager: \n" + storeManager + "\n"; } }

Driver:

Output:

image-20240214-234137.png

 

COSC-1437 / ITSE-2457 Computer Science Dept. - Author: Dr. Kevin Roark