I was going through below link to figure out differentiation between Composition and Aggregation.
https://www.geeksforgeeks.org/association-composition-aggregation-java/
I am able to understand that Composition implies a relationship where the child cannot exist independent of the parent while Aggregation implies a relationship where the child can exist independently of the parent. But not able to understand how can i differentiate that programmatically . Below is an example of Aggregation and Composition as given in link.In both cases the classes are same in structure except that Student and Department class has an extra variable "name" .As in Composition "child cannot exist independent of the parent ",but here I can create a separate object of Book and use it without adding it to Library.
Aggregation
// student class
class Student
{
String name;
int id ;
String dept;
Student(String name, int id, String dept)
{
this.name = name;
this.id = id;
this.dept = dept;
}
}
/* Department class contains list of student
Objects. It is associated with student
class through its Object(s). */
class Department
{
String name;
private List<Student> students;
Department(String name, List<Student> students)
{
this.name = name;
this.students = students;
}
public List<Student> getStudents()
{
return students;
}
}
Composition
class Book
{
public String title;
public String author;
Book(String title, String author)
{
this.title = title;
this.author = author;
}
}
// Libary class contains
// list of books.
class Library
{
// reference to refer to list of books.
private final List<Book> books;
Library (List<Book> books)
{
this.books = books;
}
public List<Book> getTotalBooksInLibrary()
{
return books;
}
}
As far as I can tell (and maybe somebody else can give a better answer), you can't evaluate if the relationship is aggregation or composition just by looking at Java code. It's the other way around.
First you create a conceptual model of the world. Libraries have books, and cars have wheels. Then you think - does it make sense for a book to exist without a library, or for a wheel to exist without a car, in the context I'm working in. So for example if you are writing a car racing game, you will have no use of wheels outside of cars. But if you are writing some auto-repair application, you will deal with wheels independently of some particular car.
So first you decide if you need aggregation or composition, and then implement it in your code. The implementation could be that object Car has List<Wheel> but you can't tell if it's composition or aggregation just from that. The key is that you interpret the code (implementation) based on your conceptual model and then use it according to that.
If it's composition, the usage it might have some restrictions:
No object other than Car will hold a reference to Wheel.
Wheel might even be a private or package-private class.
If Car is saved in database, when you delete it, you also automatically delete all of its Wheels.
But it's up to you to enforce these restrictions if you decide it's composition.
In the real world, a book can indeed exist in its own right without being owned by a library. But what if, instead, you had a LibraryBook class with fields like dateAcquired and currentBorrower? Using your design, you would still be able to create a LibraryBook instance without a library.
This is where languages like C++ can be more explicit about composition: in C++, an object can hold its parts by value. In Java, every object is handled by a pointer (OK, Java people don't call them pointers; they call them references instead.) This makes it more difficult to differentiate between composition and aggregation. In Java, you do it using careful design.
For example, we can make the LibraryBook class only instantiable through a method of Library:
class Library {
class LibraryBook {
private LibraryBook() {/*private constructor prevents independent instantiation*/}
}
LibraryBook createBook(String title, etc...);
}
Furthermore, if we make LibraryBook's mutator methods only accessible to the Library class, we can ensure that the book remains part of its owning library.
Related
I have an object that I want to populate with information. I retrieve the information from a number of different services. I made a helper class that has one public method and then has a number of private methods that do the work to call the services. What I have written works fine but I'm not sure if it is the correct way to do this.
You may be wondering why I need an object holding all this information. I need it all in one object because I create a json object from this java object and pass that to the javascript layer.
What is wrong with my approach and is there a programming paradigm I should be following to do something like this?
Example:
Person object with getters and setters for firstName, lastName, age, height, weight, list of favourite foods, list of favourite countries, list of comments.
Service 1 gives firstName, lastName, age, height and weight
Service 2
gives list of favourite countries and list of favourite foods
Service
3 gives a list of the comments made by the person
I have a personHelper class that looks like this:
public class PersonHelper{
public Person getPerson(userDetails){
Person person = new Person();
this.setPersonDetails(person, userDetails);
this.setFavourites(person, userDetails);
this.setComments(person, userDetails);
return person;
}
private Person setPersonalDetails(Person person, UserDetails userDetails){
returnedObj = callToService1(userDetails);
person.setFirstName(returnedObj.getFirstName());
person.setLastName(returnedObj.getLastName());
person.setAge(returnedObj.getAge());
person.setHeight(returnedObj.getHeight();
person.setWeight(returnedObj.getWeight());
return person;
}
private Person setFavourites(Person person, UserDetails userDetails){
<List>favsList = callToService2(userDetails);
person.setFavourites(returnedObj.getFavs(favsList));
return person;
}
private Person setComments(Person person, UserDetails userDetails){
<List>commentsList = callToService3(userDetails);
person.setComments(returnedObj.getComments(commentsList));
return person;
}
}
and then in my controller I call
person = personHelper.getPerson(userDetails);
jsonResponse = jsonProcessor.writeAsString(person);
return jsonResponse; // returns the ajax response to js
Thanks in advance for any help or suggestions.
EDIT: After more research I found that the object I am populating is referred to as a Data Transfer Object and I am populating it using the Java Bean method.
There's a trend these days to limit the mutability of objects so your setter-based approach, although workable, is sometimes not seen as the best way to create an object, even a data transfer type of object. One other thing to consider is how many objects know about each other and how much they know - it seems your PersonHelper class needs to know pretty much everything about UserDetails and Person. So if you add a field to Person, you need to add it to UserDetails and also add to PersonHelper to get that field populated.
For your type of object, you might find the Builder pattern useful. A builder is a short-term transient object designed to gather data for construction. Often the builder will have a fluent API, and gets passed to the (private) constructor of the transfer class. That means that all your code responsible for building the object is clear that that is its responsibility because it works with a Builder. Meanwhile, the constructed transfer object is effectively immutable and it becomes significantly easier to reason about the thread-safety of your code and to understand what values something might have at different parts.
public class Person {
private final String firstName;
private final String lastName;
private Person(final PersonBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
}
... usual getters etc ...
public static class PersonBuilder {
private String firstName;
private String lastName;
private PersonBuilder() {
}
public PersonBuilder withFirstName(final String name) {
this.firstName = name;
return this;
}
public PersonBuilder withLastName(final String name) {
this.lastName = name;
return this;
}
public Person build() {
return new Person(this);
}
}
public static PersonBuilder newPerson() {
return new PersonBuilder();
}
}
In this example the builder is a little over-wieldy, but when you've got twenty or thirty different pieces of data which are somehow optional it can make sense and makes for very easy to read construction code...
Person.newPerson().withFirstName("Sam").withLastName("Spade").build()
It seems to me that your 'UserDetails' object could be turned into a kind of builder. And so your 'PersonHelper' class would end up just calling userDetails.build() rather than knowing all about what fields the Person object (and userDetails object) contains.
There is no general paradigm for your question, but here are a few tips for your design:
It seems that your person data (names, favourites) is distributed among several data stores and you have to gether it all in your PersonHelper class. I don't know if this services are used anywhere else, but from the controller point of view this helper should be a service too.
Since your service invocations are independent, you can execute them in parallel
For some kind of applications it can be even better if you expose these services for UI level. For example, if data is presented in different UI blocks, client can make several asynchronous requests and display the data as soon as responses are received.
Please look at the code below
Class Employee{
private String name;
private String id;
public String getName(){ return name; }
public void setName(String name){ this.name = name; }
public String getId(){ return id; }
public void setId(String id){ this.id = id; }
}
Class Teacher extends Employee{
private double salary;
}
Now my question is If I am creating an object of Teacher , then it does not make sense without the Teacher object having a name and id. I can set the same for teacher object via public setters of Employee but it it correct ?
Teacher t1 = new Teacher();
t1.setName("aaa");
t1.setId("224");
t1.salary = 200.00;
System.out.println(t1.toString());
I am asking this question as my understanding is if the field is private it should be used only via getters . But in the example provided above Teacher object will not make sense without having a Name or Id .
If it is correct then why not make the field public in the first place? What is the advantage in using it private and then allowing access via public setter ?
If it is not correct please provide an example of how the above Employee and Teacher class should be implemented ?
Your question seem to show a confusion between two concepts rather independant:
encapsulation
creation of objects
Encapsulation: it is better design to define private variables. Then you can not corrupt the object from outside. You must use setter to modify your employee.
But, if you trust Teacher, it could modify Employee as a subclass, without setter, it is faster to code (but little risky: if you have to change the setter in employee, Teacher wont get it, ...).
Creation of objects: you should pass certain values to the variables, or they are defined by default (or auto-built ...)
=> you can decide that Teacher have well defined values (default), or that you must give these values (mandatory). It is your design.
After that, you can change them directly or by setters of Employee (=> first concept of encapsulation).
then it does not make sense without the Teacher object having a name and id. I can set the same for teacher object via public setters of Employee but it it correct ?
This is where exactly constructor comes into picture. You need to pass them before you are using it.
Thumbrule : When you want something while building it, you need to force them to pass on constructor.
I was reading about Spring and encountered an example consisting of an abstract product class with name and price fields.
Next, there is a Battery class which extends the Product class and adds a rechargable field. Then, a CDDrive class (also) extending Product but adding a capacity field.
In the real world when we often have products having many disparate attributes, how does one model arbitrary products with arbitrary properties and fields?
Does having a class for each product make sense?
So, can you guys please suggest a pattern for achieving this?
Thanks,
Ouney
Good question. We had a similar situation before where we had GUI components that shared many of their abstract parent, but each page had its own set of labels that weren't shared by others. The standoff was on. We found it silly to just keep creating subclasses because of the mutually disjoint properties they had. What did it for us was maps. First, to have a subclass is to have one or more distinguishing properties that are fist class objects. Rechargeable for batteries and capacity for cd drives in your case. Then for the properties one can't think of at the time of building, or simply differ in minor naming conventions, use maps. I demonstrate with the example below.
The product:
public abstract class Product {
String name;
Double price;
Map<String, Object> propMap;
public Product(String name, Double price) {
this.name = name;
this.price = price;
propMap = new HashMap<>();
}
public void add2propMap(String key, Object value) {
propMap.put(key, value);
}
public String toString() {
return "Product [name=" + name + ", price=" + price + ", propMap=" + propMap + "]";
}
}
The CdDrive:
public class CdDrive extends Product {
String capacity;
public CdDrive(String name, Double price, String capacity) {
super(name, price);
this.capacity = capacity;
}
}
The Battery:
public class Battery extends Product {
Boolean rechargable;
public Battery(String name, Double price, Boolean rechargable) {
super(name, price);
this.rechargable = rechargable;
}
}
Then a client:
public class Client {
public static void main(String[] args) {
List<Product> productList = new ArrayList<>();
Battery energizer = new Battery("Energizer", 12d, true);
energizer.add2propMap("numInPackage", new Integer(8));
energizer.add2propMap("make", "US");
productList.add(energizer);
CdDrive superDrive = new CdDrive("Apple Drive", 200d, "200 GB");
superDrive.add2propMap("type", "External");
superDrive.add2propMap("expandable", false);
productList.add(superDrive);
productList.forEach(p -> System.out.println(p));
}
}
Which gives this when run:
Product [name=Energizer, price=12.0, propMap={numInPackage=8, make=US}]
Product [name=Apple Drive, price=200.0, propMap={expandable=false, type=External}]
This setup made the architecture scalable, maintainable and modifiable. The map keys always reported what was in there in case in doubt. Adding is easy and so is modifying.
Does having a class for each product make sense?
In real life situation, it rarely makes sense. They are just making up some example to make you get the feeling of it.
Just imagine your online shop sells CD players, now you want to add some MD players in your product list, and you need to change your code and redeploy the application just because of it. Non-sense huh?
Unless you have bunch of specific function for some specific type of product, having a dedicated class for such type of product will make sense. (e.g. Product, PhysicallyDeliverableProduct something like that. Still there are better way to design it though)
In real life, the way to solve the issue in your question, is mostly by designing your Product to keep some arbitrary properties (e.g. keeping a Map<String,Object>, so you can put ["rechargeable", true] for a battery you add on your site.
Design pattern? I think what you are looking for is still far from required to make use of patterns. Personally I will suggest you to take a look on the book "Analysis Pattern" by Martin Fowler. You may not be able to use the design in it directly, but it give you feel on what real life design looks like
Does having a class for each product make sense?
To me it absolutely makes sense to have separate classes for separate products.
That makes your code more loosely coupled. In future if you want to change the implementation of a particular product, changing the code won't mess up the implementation of other products if you have a separate class for that. The generic methods & properties you can put in an abstract class.
a pattern for achieving this?
You might want to look at the Factory & template pattern.
You can create an interface Product & all the classes will implement that interface & define their own implementations.
Use abstract class only when you want to provide a default behaviour to your methods. For an instance have a look at the template pattern here.
An abstract class game is created which defines the play method. initialize & startPlay etc can have their respective definition in the subclasses but the play method will always run the other methods.
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//template method
public final void play(){
//initialize the game
initialize();
//start game
startPlay();
//end game
endPlay();
}
}
If you don't intend to provide any default behaviour rather just declare the properties & methods in an interface Product & let the classes implement that.
interface Product{
String NAME="defaultName";
Integer PRICE=5;
initialCost(); // example of a generic method
}
//Note that name & price if you declare those in interface will be treated as constants.
class Battery implements Product{
Boolean rechargable =false;
public void initialCost(){
//method definition
}
}
class CdDrive implements Product{
Integer capacity = xxxx;
public void initialCost(){
//CdDrive method definition
}
}
You can create the objects as
Product product = new Battery();
Product nextProduct = new CdDrive();
this makes your code loosely coupled. Also known as programming to an interface.
I'm aware of the conceptual differences between Aggregation and Composition. Can someone tell me the implementation difference in Java between them with examples?
Composition
final class Car {
private final Engine engine;
Car(EngineSpecs specs) {
engine = new Engine(specs);
}
void move() {
engine.work();
}
}
Aggregation
final class Car {
private Engine engine;
void setEngine(Engine engine) {
this.engine = engine;
}
void move() {
if (engine != null)
engine.work();
}
}
In the case of composition, the Engine is completely encapsulated by the Car. There is no way for the outside world to get a reference to the Engine. The Engine lives and dies with the car. With aggregation, the Car also performs its functions through an Engine, but the Engine is not always an internal part of the Car. Engines may be swapped, or even completely removed. Not only that, but the outside world can still have a reference to the Engine, and tinker with it regardless of whether it's in the Car.
I would use a nice UML example.
Take a university that has 1 to 20 different departments and each department has 1 to 5 professors.
There is a composition link between a University and its' departments.
There is an aggregation link between a department and its' professors.
Composition is just a STRONG aggregation, if the university is destroyed then the departments should also be destroyed. But we shouldn't kill the professors even if their respective departments disappear.
In java :
public class University {
private List<Department> departments;
public void destroy(){
//it's composition, when I destroy a university I also destroy the departments. they cant live outside my university instance
if(departments!=null)
for(Department d : departments) d.destroy();
departments.clean();
departments = null;
}
}
public class Department {
private List<Professor> professors;
private University university;
Department(University univ){
this.university = univ;
//check here univ not null throw whatever depending on your needs
}
public void destroy(){
//It's aggregation here, we just tell the professor they are fired but they can still keep living
for(Professor p:professors)
p.fire(this);
professors.clean();
professors = null;
}
}
public class Professor {
private String name;
private List<Department> attachedDepartments;
public void destroy(){
}
public void fire(Department d){
attachedDepartments.remove(d);
}
}
Something around this.
EDIT: an example as requested
public class Test
{
public static void main(String[] args)
{
University university = new University();
//the department only exists in the university
Department dep = university.createDepartment();
// the professor exists outside the university
Professor prof = new Professor("Raoul");
System.out.println(university.toString());
System.out.println(prof.toString());
dep.assign(prof);
System.out.println(university.toString());
System.out.println(prof.toString());
dep.destroy();
System.out.println(university.toString());
System.out.println(prof.toString());
}
}
University class
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class University {
private List<Department> departments = new ArrayList<>();
public Department createDepartment() {
final Department dep = new Department(this, "Math");
departments.add(dep);
return dep;
}
public void destroy() {
System.out.println("Destroying university");
//it's composition, when I destroy a university I also destroy the departments. they cant live outside my university instance
if (departments != null)
departments.forEach(Department::destroy);
departments = null;
}
#Override
public String toString() {
return "University{\n" +
"departments=\n" + departments.stream().map(Department::toString).collect(Collectors.joining("\n")) +
"\n}";
}
}
Department class
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Department {
private final String name;
private List<Professor> professors = new ArrayList<>();
private final University university;
public Department(University univ, String name) {
this.university = univ;
this.name = name;
//check here univ not null throw whatever depending on your needs
}
public void assign(Professor p) {
//maybe use a Set here
System.out.println("Department hiring " + p.getName());
professors.add(p);
p.join(this);
}
public void fire(Professor p) {
//maybe use a Set here
System.out.println("Department firing " + p.getName());
professors.remove(p);
p.quit(this);
}
public void destroy() {
//It's aggregation here, we just tell the professor they are fired but they can still keep living
System.out.println("Destroying department");
professors.forEach(professor -> professor.quit(this));
professors = null;
}
#Override
public String toString() {
return professors == null
? "Department " + name + " doesn't exists anymore"
: "Department " + name + "{\n" +
"professors=" + professors.stream().map(Professor::toString).collect(Collectors.joining("\n")) +
"\n}";
}
}
Professor class
import java.util.ArrayList;
import java.util.List;
public class Professor {
private final String name;
private final List<Department> attachedDepartments = new ArrayList<>();
public Professor(String name) {
this.name = name;
}
public void destroy() {
}
public void join(Department d) {
attachedDepartments.add(d);
}
public void quit(Department d) {
attachedDepartments.remove(d);
}
public String getName() {
return name;
}
#Override
public String toString() {
return "Professor " + name + " working for " + attachedDepartments.size() + " department(s)\n";
}
}
The implementation is debatable as it depends on how you need to handle creation, hiring deletion etc. Unrelevant for the OP
A simple Composition program
public class Person {
private double salary;
private String name;
private Birthday bday;
public Person(int y,int m,int d,String name){
bday=new Birthday(y, m, d);
this.name=name;
}
public double getSalary() {
return salary;
}
public String getName() {
return name;
}
public Birthday getBday() {
return bday;
}
///////////////////////////////inner class///////////////////////
private class Birthday{
int year,month,day;
public Birthday(int y,int m,int d){
year=y;
month=m;
day=d;
}
public String toString(){
return String.format("%s-%s-%s", year,month,day);
}
}
//////////////////////////////////////////////////////////////////
}
public class CompositionTst {
public static void main(String[] args) {
// TODO code application logic here
Person person=new Person(2001, 11, 29, "Thilina");
System.out.println("Name : "+person.getName());
System.out.println("Birthday : "+person.getBday());
//The below object cannot be created. A bithday cannot exixts without a Person
//Birthday bday=new Birthday(1988,11,10);
}
}
In simple terms :
Both Composition and Aggregation are Associations.
Composition -> Strong Has-A relationship
Aggregation -> Weak Has-A relationship.
There is a great explanation in the given url below.
http://www.codeproject.com/Articles/330447/Understanding-Association-Aggregation-and-Composit
Please check!!!
First we must talk about what actually the difference between Aggregation and Composition is to be on the same page.
Aggregation is an association where the associated entity may exist independent of the association. For example, a Person may be associated to an Organisation but he/she may have independent existence in the system.
whereas
Composition refers to a situation when one of the associated entities is strongly related to the other and cannot exist without the other's existence. In fact the identity of that entity is always associated with the identity of the other object. For example, wheels in a car.
Now, aggregation can simply be achieved by holding a property of one entity in another as below:
class Person {
Organisation worksFor;
}
class Organisation {
String name;
}
class Main {
public static void main(String args[]) {
//Create Person object independently
Person p = new Person();
//Create the Organisation independently
Organisation o = new Organisation();
o.name = "XYZ Corporation";
/*
At this point both person and organisation
exist without any association
*/
p.worksFor = o;
}
}
For Composition it is necessary that the dependent object is always created with the identity of its associated object. You can use an inner class for the same.
class Car {
class Wheel {
Car associatedWith;
}
}
class Main {
public static void main() {
//Create Car object independently
Car car = new Car();
//Cannot create Wheel instance independently
//need a reference of a Car for the same.
Car.Wheel wheel = car.new Wheel();
}
}
Please note that the same use case may fall under aggregation/composition depending on the application scenario. For example, the Person-Organisation case may become composition if you are developing an application for people working in some organisation and the reference to organisation is must for sign up. Similarly, if you are maintaining inventory for parts of a Car, Car-Wheel relationship can be aggregation.
The difference is that any composition is an aggregation and not vice versa.
Let's set the terms. The Aggregation is a metaterm in the UML standard, and means BOTH composition and shared aggregation, simply named shared. Too often it is named incorrectly "aggregation". It is BAD, for composition is an aggregation, too. As I understand, you mean "shared".
Further from UML standard:
composite - Indicates that the property is aggregated compositely,
i.e., the composite object has responsibility for the existence and
storage of the composed objects (parts).
So, University to cathedras association is a composition, because cathedra doesn't exist out of University (IMHO)
Precise semantics of shared aggregation varies by application area and
modeler.
I.e., all other associations can be drawn as shared aggregations, if you are only following to some principles of yours or of somebody else. Also look here.
Aggregation vs Composition
Aggregation implies a relationship where the child can exist independently of the parent. For example, Bank and Employee, delete the Bank and the Employee still exist.
whereas Composition implies a relationship where the child cannot exist independent of the parent. Example: Human and heart, heart don’t exist separate to a Human.
Aggregation relation is “has-a” and composition is “part-of” relation.
Composition is a strong Association whereas Aggregation is a weak Association.
Both types are of course associations, and not really mapped strictly to language elements like that. The difference is in the purpose, context, and how the system is modeled.
As a practical example, compare two different types of systems with similar entities:
A car registration system that primarily keep track of cars, and their owners, etc. Here we are not interested in the engine as a separate entity, but we may still have engine related attributes, like power, and type of fuel. Here the Engine may be a composite part of the car entity.
A car service shop management system that manages car parts, servicing cars, and replace parts, maybe complete engines. Here we may even have engines stocked and need to keep track of them and other parts separately and independent of the cars. Here the Engine may be an aggregated part of the car entity.
How you implement this in your language is of minor concern since at that level things like readability is much more important.
I have to build a library management system and i've run into problems while trying to implement user types or profiles. I've already got a superclass user and two other subclasses of User, Student and Teacher, each with their own "characteristics". The thing is i have to implement 7 types of users (5 types of students and 2 types of clerks) based on the number of books they can borrow and the amount of time they can keep the books until they have to return them. Those are the only 2 differences between the classes.
How would you implement this? Inheritance? I'm looking for a clever way to implement this and i would love to hear your thoughts on this.
Thank you very much.
As a good rule of thumb, anywhere you see a noun in a project specification it's a good candidate for a class. If those nouns have relationships in the project spec, they probably aught to have one in your code too.
All of your people would fit in the category of a Userso perhaps this should be an interface they would all inherit. Down from this they appear to fit into two categories, Student and Staff perhaps these should also be abstract classes / interfaces. Then you have your 7 concrete classes. 2 inheriting Staff and 5 inheriting Student.
So you'd end up with something like this..
Of course, this design depends on what every User must do, what every Staff / Student must do but I'll leave the very specific details to you.
You have a "class" per person, which really limits your design; because, if you want to add a student or teacher, you need to start writing a new class.
Classes are templates, and each template is used to construct an "instance of the class" or more specifically an "instance". One template is typically used to construct more than one class (although it is not necessary for a class to be used more than once, using it once (or not using it at all) is fine).
So you could do
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public string getName() {
return this.name;
}
}
public class Staff {
private String name;
public Staff(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
As you can see, there is going to be a lot of duplication between staff and students. getName(), getAge(), getPhoneNumber(), getAddress(), etc can easily be applied to both, which under this structure means that you would have to duplicate those methods for both Student and Staff.
What does both a staff member and a student have in common? They are both People, and many of the common methods are common to all people.
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
public Staff extends Person {
public void teachClass(Class class) {
...
}
}
public Student extends Person {
public void attendClass(Class class) {
...
}
}
This design also creates other issues, as it implies that a Staff member is not a Student, and a Student is not a Staff member. In the real world, sometimes the Staff enrolls for classes, and Students can take on teaching roles (think teacher's aide).
The most flexible method actually doesn't create a structural differentiation between a Student and Staff, it differentiates between the two by ability.
public class Person {
public Person(String name) {
...
}
public void canTeach(Course course) {
teaching.add(course);
}
public void attending(Course course) {
attending.add(course);
}
public boolean isStaff() {
return !teaching.isEmpty();
}
public boolean isStudent() {
return !attending.isEmpty();
}
}
However, this structure is radically different from the example you are being presented in class, and it side-steps the lessons you really are supposed to be learning about inheritance.