这被视为循环依赖吗(这是良好的实践吗)?

huangapple go评论97阅读模式
英文:

Is this considered a cyclic dependency (is this good practice)?

问题

以下是翻译好的部分:

嗨,我是一个新的Java程序员,有一个关于类设计的小问题。

我明白这样的东西是循环依赖,可能不是项目结构的一种方式:

public class Course {
    private ArrayList<Student> students;

    public Course (ArrayList<Student> students) {
        this.students = students;
    }
}

public class Student {
    private Course course;

    public Student (Course course) {
        this.course = course;
    }
}

但是如果将Student.java更改为:

public class Student {
    private int courseId;

    public Student (int courseId) {
        this.courseId = courseId;
    }
}

这样courseId可以用于从DAO或其他地方检索课程。这仍然是一个好的结构吗?因为现在每个Course仍然“关心”学生,每个学生仍然“关心”课程。

英文:

Hi I am a new Java programmer and have a small question about class design.

I understand that something like this is a cyclic dependency and is probably not a way to structure a project:

public class Course {
    private ArrayList&lt;Student&gt; students;

    public Course (ArrayList&lt;Student&gt; students) {
        this.students = students;
    }
}


public class Student {
    private Course course;

    public Student (Course course) {
        this.course = course;
    }
}

But what if Student.java is changed to:

public class Student {
    private int courseId;

    public Student (int courseId) {
        this.courseId = courseId;
    }
}

so that courseId can be used to retrieve the course from a DAO or something. Is this still a good structure? Since now each Course still cares about Students. and each Student still cares about Courses.

答案1

得分: 6

Reciprocal references are fine, all database entities exist side-by-side referring to each other.
相互引用是可以的,所有数据库实体都可以相互引用。

The constructors must allow the creation, not being barred that they only can be created if the other already exists.
构造函数必须允许创建,不能限制它们只能在另一个已经存在的情况下创建。

Hence maybe two constructors.
因此可能需要两个构造函数。

public class Course {
public class Course {
public class Course {
public class Course {
private List students = new ArrayList<>();
private List students = new ArrayList<>();
private List students = new ArrayList<>();
private List students = new ArrayList<>();

public Course() {
public Course() {
public Course() {
public Course() {
}

public Course (List students) {
public Course (List students) {
public Course (List students) {
public Course (List students) {
this.students.addAll(students);
this.students.addAll(students);
this.students.addAll(students);
this.students.addAll(students);
}
}

public class Student {
public class Student {
public class Student {
public class Student {
private Course course;
private Course course;
private Course course;
private Course course;

public Student (Course course) {
public Student (Course course) {
public Student (Course course) {
public Student (Course course) {
this.course = course;
this.course = course;
this.course = course;
this.course = course;
course.addStudent(this);
course.addStudent(this);
course.addStudent(this);
course.addStudent(this);
}
}
}

With databases one often has numeric IDs, but many Object/Relational mappings can still allow you the classes above, under the hood using the IDs (JPA, eclipseLink, hibernate). It is not needed to have assymetrically IDs and object references.
在数据库中,通常会使用数字ID,但许多对象/关系映射仍然可以在底层使用这些类,使用ID(JPA、eclipseLink、hibernate)。不需要具有不对称的ID和对象引用。

You should not use concrete implementations (ArrayList) but be most flexible (List).
您不应该使用具体实现(ArrayList),而应该更加灵活(List)。

Also it might be better not to expose the field's internal data (students) for changing outside.
同时,最好不要将字段的内部数据(students)暴露给外部进行更改。

About generalisations (List) and implementations (ArrayList)

Some (scripting) languages have just one type for a collection. Java was
designed to provide several implementations for one interface.

So you can decide whether to implement a data structure for Map as fast HashMap or ordered TreeMap. In general the users only need to now about Map. You need not overspecify your code, and can even redesign simply using a different implementation class.

List list = ...
Collections.sort(list);
list.add(...);

List convert(List list) { ... }

In the code above your convert can deal with any kind of List; you have written a more generic algorithm not just for an ArrayList. And in the method you can either return an ArrayList or a LinkedList. This makes code changes due to the wrong overspecification unlikely.

Coding Against Interfaces of Nick Hodges (pardon the wrong electric outlets and TypeScript).

关于一般化(List)和实现(ArrayList

一些(脚本)语言只有一种集合类型。Java旨在为一个接口提供多种实现。

因此,您可以决定是否要实现一个作为快速HashMap或有序TreeMap的Map数据结构。通常用户只需要了解Map。您不需要过度规定您的代码,甚至可以简单地使用不同的实现类进行重新设计。

在上面的代码中,您的convert可以处理_任何_类型的List;您编写了一个更通用的算法,不仅适用于ArrayList。而且在方法中,您可以返回ArrayList或LinkedList。这使得由于错误的过度规定而导致的代码更改不太可能发生。

Nick Hodges的《针对接口编程》(抱歉,错误的电源插座和TypeScript)。

英文:

Reciprocal references are fine, all database entities exist side-by-side referring to each other.

The constructors must allow the creation, not being barred that they only can be created if the other already exists.

Hence maybe two constructors.

public class Course {
    private List&lt;Student&gt; students = new ArrayList&lt;&gt;();

    public Course() {
    }

    public Course (List&lt;Student&gt; students) {
        this.students.addAll(students);
    }
}


public class Student {
    private Course course;

    public Student (Course course) {
        this.course = course;
        course.addStudent(this);
    }
}

With databases one often has numeric IDs, but many Object/Relational mappings can still allow you the classes above, under the hood using the IDs (JPA, eclipseLink, hibernate). It is not needed to have assymetrically IDs and object references.

You should not use concrete implementations (ArrayList) but be most flexible (List).

Also it might be better not to expose the field's internal data (students) for changing outside.


About generalisations (List) and implementations (ArrayList)

Some (scripting) languages have just one type for a collection. Java was
designed to provide several implementations for one interface.

So you can decide whether to implement a data structure for Map as fast HashMap or ordered TreeMap. In general the users only need to now about Map. You need not overspecify your code, and can even redesign simply using a different implementation class.

List&lt;String&gt; list = ...
Collections.sort(list);
list.add(...);

List&lt;String&gt; convert(List&lt;String&gt; list) { ... }

In the code above your convert can deal with any kind of List; you have written a more generic algorithm not just for an ArrayList. And in the method you can either return an ArrayList or a LinkedList. This makes code changes due to the wrong overspecification unlikely.

Coding Against Interfaces of Nick Hodges (pardon the wrong electric outlets and TypeScript).

答案2

得分: 5

第一个片段可以考虑作为一个有效的设计(稍作修改,见下文)。假设你有一个学院:

  • 要注册一个学生,学生需要选择一门课程,因此构造函数为 public Student (Course course)
  • 而且一个课程有一个已注册学生的列表,但是一开始并没有学生。学生会在后面添加进来。

实际上,我可以这样使用它:

public class Course {
    private String name;
    private List<Student> students;

    public Course(String name) {
        this.name = name;
        students = new ArrayList<>();
    }

    public void addStudent(Student student) {
        students.add(student);
    }
}

public class Student {
    private Course course;

    public Student(Course course) {
        this.course = course;
    }
}

// 现在...
Course history = new Course("History");
Student historyStudent = new Student(history);
history.addStudent(historyStudent);

作为一个附注,这个设计可能有多种变体,但我的观点是,你的设计几乎是有效的,只需稍作修改,不在课程初始化时设置学生列表即可。

英文:

The first snippet can be considered a valid design (with a small modification, see below). Suppose you have an academy:

  • To enroll a Student, the student needs to pick a course, hence the constructor public Student (Course course)
  • And a Course has a list of enrolled students, BUT, it is created with no students at first. They are added later on.

In fact I could use it like this:

public class Course {
    private String name;
    private List&lt;Student&gt; students;

    public Course (String name) {
        this.name = name;
        students = new ArrayList&lt;&gt;();
    }

    public void addStudent(Student student) {
        students.add(student);
    }
}


public class Student {
    private Course course;

    public Student (Course course) {
        this.course = course;
    }
}

/// -- now...

Course history = new Course(&quot;History&quot;);
Student historyStudent = new Student(history);
history.add(historyStudent);

As a side note, there may be multiple variations of this design, but my point is, your design is almost valid, except for the small modification of not setting the list of students on course initialization.

答案3

得分: 5

你可以诚实地以DAO / POJO的方式更好地完成此操作,将课程和学生分开。

如果你正在使用SQL数据库,你可能会设置以下表格:

  • 课程(Course)
  • 学生(Student)
  • 注册(Enrollment)

其中注册(Enrollment)包括课程(Course)、学生(Student)以及开始和可能的结束日期。这样,课程(Course)和学生(Student)就解耦了。

我建议你将你的类模型化为这样。

英文:

You'd honestly be better served doing this in a DAO / POJO way, and decoupling your Courses and your Students

If you were using a SQL database, you'd probably set up tables like

Course
Student
Enrollment

Where Enrollment has a Course, a Student, and a start and maybe an end date. This way, Course and Student are decoupled.

I recommend you model your classes like this

答案4

得分: 4

在模型中,特别是在有数据库支持的情况下,这种循环依赖并不被视为不好。实际上,在ORM(可能在所有Java项目中都会看到类似的情况)中,这是非常常见的。

另一方面,使用courseId的选项则不太好:它泄漏了模型的技术细节(如果它旨在反映某个代理主键),引入了Java领域中。

标准免责声明:仅仅根据几个代码片段来判断软件架构是“好”还是“坏”,就好像根据一块砖来评判建筑架构一样。

英文:

In models, especially backed by a database - this type of cyclic dependency is not considered bad. In fact, it's very common with ORMs (probably in 95% of all Java projects you would see something like that).

The option with courseId is - on the other hand - is a worse idea: it leaks a technical aspect of the model (if it's meant to reflect some surrogate primary key) into the Java domain.

Standard disclaimer: calling software architecture "good" or "bad" on a basis of a couple of snippets is like judging a building architecture based on a brick.

huangapple
  • 本文由 发表于 2020年8月12日 20:57:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/63377040.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定