I’m using the Decorator Pattern in one of my programs to decorate Courses with different Graduation Requirements. A few examples:
new BasicCourse("Course Foo", 1); // parameters are name and credits
new DisciplinaryBreadth(DBR.MATH, new BasicCourse("Course Foo", 1));
new IntroToHumanities(IHUM.FIRST_QUARTER,
new ProgramInWritingAndRhetoric(PWR.FIRST_YEAR,
new BasicCourse("Course Bar", 2)));
new ProgramInWritingAndRhetoric(PWR.FIRST_YEAR,
new IntroToHumanities(IHUM.FIRST_QUARTER,
new BasicCourse("Course Bar", 2)));
The last two are the same by the commutative property.
I am implementing hashcode() and equals(), so that I can use the Courses in a HashMap. I had to change Eclipse’s auto-generated equals function, so that Courses that were commutative would equal each other. I don’t understand hashcode generation as well as I do equals, so I am wondering if I need to do the same for the hashcode function.
Here’s what Eclipse gave me for each of the decorators:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((inner == null) ? 0 : inner.hashCode());
result = prime * result
+ ((requirement == null) ? 0 : requirement.hashCode());
return result;
}
Will this produce the same hash for commutative Courses?
EDIT
Each decorator has a requirement variable that corresponds to the requirement the Course with that decorator fulfills. For example, the DisciplinaryBreadth class has a requirement of class DBR, which is an enum of the possible Disciplinary Breadth requirements. The ProgramInWritingAndRhetoric class has a requirement of class PWR, which is an enum of the possible Program in Writing and Rhetoric requirements. And so on.
The requirement variable in each decorator is a different enum. All the Courses implement methods that get their various Graduation Requirements (e.g. getDbrs(), getIhum()).
The decorators call the method of their inner Course for all the requirements except the one they define. For example the IntroToHumanities class calls inner.getDbrs() and inner.getPwr(), but uses its requirement variable for its getIhum() method. They also call the method of their inner Course for the getName() and getCredits() methods, while the BasicCourse class defines those methods using final members set in its constructor.
The inner variable is just the inner Course each decorator wraps. The inner and requirement variables are set in the constructors of each decorator and are final.
Here’s my equals method for the ProgramInWritingAndRhetoric class:
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Course))
return false;
Course other = (Course) obj;
if (getName() == null) {
if (other.getName() != null)
return false;
} else if (!getName().equals(other.getName()))
return false;
if (getCredits() != other.getCredits())
return false;
if (!getDbrs().equals(other.getDbrs()))
return false;
if (!requirement.equals(other.getPwr()))
return false;
if (!getIhum().equals(other.getIhum()))
return false;
if (!getEc().equals(other.getEc())) //another Graduation Requirement
return false;
return true;
}
I skipped some of the checks for null because I check them in the constructors, so you can’t make a Course with a null IHUM, for example. The other Graduation Requirements implement equals similarly, the only difference being where the requirement variable is used.
@Eva I wouldn’t agree with the advice that you should let your IDE generate your hashcode method, but maybe I’m old fashioned.
Basically when generating a hash you’re aiming to get (as far as reasonably practicable) a well distributed and unique set of hashes based on the member variables of your object.
Therefore you should be taking the value (where primitive) or hashcode (when a complex object) of your member variables and using them to calculate the hashcode in a manner that would be well distributed (hence the common use of a prime number as a multiplier) and unlikely to cause collision.
A reasonable hashCode() would therefore use the same member variables as your equals method, but taking the values / hash values and multiplying them together along with a prime.
Bear in mind that the only guarantee of a unique, well distributed hash is to know the full universe of objects possible in advance, in which case you could actually preassign the hash, but that is rarely a valid solution.
This is quite a good explanation of what you’re trying to achieve – http://eclipsesource.com/blogs/2012/09/04/the-3-things-you-should-know-about-hashcode/