这段代码如何违反了德米特法则?

发布于 2024-08-28 11:15:28 字数 2004 浏览 4 评论 0原文

以下代码违反了德墨忒尔定律

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;
  }
}

问题

  1. LoD坏了?
  2. 破坏 LoD 的代码在哪里?
  3. 应该如何编写代码来维护 LoD?

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

  1. How is the LoD broken?
  2. Where is the code breaking the LoD?
  3. How should the code be written to uphold the LoD?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

沉溺在你眼里的海 2024-09-04 11:15:28

我认为这里有两个问题:

  1. Grades 逻辑与 Student 混合太多。它应该在 Grades 类中完成,
  2. Teacher 的逻辑被放置在 Student 中。

结论:学生对教师和成绩的内部结构和逻辑了解太多,从而破坏了 LoD

I think that here are two problems:

  1. Grades logic is too much mixed with Student. It should be done in Grades class
  2. Teacher's logic is placed into Student.

Conclusion: Student knows too much about inner structure and logic of Teacher and Grades and that breaks LoD

自演自醉 2024-09-04 11:15:28

诸如此类的大多数问题都可以通过重新访问域模型来解决。

看起来学生的责任比应有的要多得多。它应该只有一个改变的理由。

我将通过添加 ReportCard 对象来重构它。

public class ReportCard
{
  public Student Student...
  public int Year...
  public ReportCardItem[] ReportCardItems...

  getGrades()...
  createGrades()...
}

public class ReportCardItem
{
  public Grade Grade...
  public string Subject...
  public Teacher Teacher...
}

Most problems such as this can be solved by revisiting your domain model.

It looks like the Student has way more responsibility than it should. It should have only one reason to change.

I would refactor this by adding a ReportCard object.

public class ReportCard
{
  public Student Student...
  public int Year...
  public ReportCardItem[] ReportCardItems...

  getGrades()...
  createGrades()...
}

public class ReportCardItem
{
  public Grade Grade...
  public string Subject...
  public Teacher Teacher...
}
鹤仙姿 2024-09-04 11:15:28

Student 类中的方法违反了 Demeter 定律,

private Grades getGradesForYear( int year )
private Teacher getTeacher( int year )

因为这些方法向应用程序公开了域对象 Grades 和 Teacher。

假设您希望继续隐藏学生中的成绩和成绩中的教师,解决此问题的一种方法是在学生类中定义代理方法(也称为委托方法),代表内部成绩和教师对象进行操作应用程序的,类似于方法 Student.isTeacher(int, String)。此解决方案可能会导致学生中的“成绩”和“教师”中的方法重复,这是一个缺点尊重 LofD 的类设计。

更好的解决方案是从学生中删除成绩和教师,并将它们全部放在另一个班级中,成绩单说:

class Transcript {
  Student student;
  Teacher teacher;
  Grades grades;
  Integer year;
}  

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;
}  
女中豪杰 2024-09-04 11:15:28

根据您提到的维基百科文章,Person.isTeacher“到达”。

我惊讶地发现成绩列表是学生的财产。难道这不应该是学校了解和管理的事情吗?我会问学校,哪位老师哪一年给学生评分...

Person.isTeacher "reaches through" according to the wikipedia article you mention.

I was surprised to find the list of grades a property of the student. Shouldn't that be something the school knows about and manages? I'd ask the school, which teacher graded a student in which year...

〆一缕阳光ご 2024-09-04 11:15:28

通过拥有这两个私有函数会破坏 LoD。

private Grades getGradesForYear( int year )
private Teacher getTeacher( int year )

学生不应该需要逻辑来执行此类任务。

我重新设计的方法是将数据与逻辑分开。学生应该纯粹是一个数据而已。它应仅包含有关学生和学生的信息。因此,这不包括成绩,因为该概念需要其他概念,例如科目和教师。

对于老师来说也是如此。然后,我将创建一个存储成绩信息的位置和另一个存储科目信息的位置。

要执行类似的任务,我会这样做:

gradesDatabase.getGrade(subject, student);
subjectDatabase.getTeacher(subject, student);

其中主体也是一个仅数据的对象。

By having these two private functions breaks LoD.

private Grades getGradesForYear( int year )
private Teacher getTeacher( int year )

Students shouldn't need the logic to to perform such tasks.

The way I would redesign this is to separate data from logic. Student should purely be a data only. It should contain information about the student and student only. Therefore this does not include Grades as that concept requires others such as subject, and teacher.

The same goes for teacher. I would then create a place to store grade information and another place for subject information.

To perform similar tasks I would do this:

gradesDatabase.getGrade(subject, student);
subjectDatabase.getTeacher(subject, student);

Where subject is also a data only object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文