The following code breaks the Law of Demeter:
public class Student extends Person {
private Grades grades;
public Student() {
}
/** Must never return null; throw an appropriately named exception, instead. */
private synchronized Grades getGrades() throws GradesException {
if( this.grades == null ) {
this.grades = createGrades();
}
return this.grades;
}
/** Create a new instance of grades for this student. */
protected Grades createGrades() throws GradesException {
// Reads the grades from the database, if needed.
//
return new Grades();
}
/** Answers if this student was graded by a teacher with the given name. */
public boolean isTeacher( int year, String name ) throws GradesException, TeacherException {
// The method only knows about Teacher instances.
//
return getTeacher( year ).nameEquals( name );
}
private Grades getGradesForYear( int year ) throws GradesException {
// The method only knows about Grades instances.
//
return getGrades().getForYear( year );
}
private Teacher getTeacher( int year ) throws GradesException, TeacherException {
// This method knows about Grades and Teacher instances. A mistake?
//
return getGradesForYear( year ).getTeacher();
}
}
public class Teacher extends Person {
public Teacher() {
}
/**
* This method will take into consideration first name,
* last name, middle initial, case sensitivity, and
* eventually it could answer true to wild cards and
* regular expressions.
*/
public boolean nameEquals( String name ) {
return getName().equalsIgnoreCase( name );
}
/** Never returns null. */
private synchronized String getName() {
if( this.name == null ) {
this.name == "";
}
return this.name;
}
}
Questions
Methods in class Student which break the Law of Demeter are
private Grades getGradesForYear( int year )
private Teacher getTeacher( int year )
because these expose domain objects Grades and Teacher to the application.
Assuming that you wish to continue to hide the Grades inside a Student and a Teacher inside Grades, one way to remedy this problem is to define proxy methods (also called delegate methods) in class Student that operate on the internal Grades and Teacher objects on behalf of the application, similar to method Student.isTeacher(int, String)
. This solution may lead to duplication of methods in Grades and Teacher in Student which is a disadvantage of a class design which respects the LofD.
A better solution would be to remove the Grades and Teacher from Student and put them all in another class, say Transcript:
class Transcript {
Student student;
Teacher teacher;
Grades grades;
Integer year;
}