Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

oop

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<Student> students;

    public Course (ArrayList<Student> 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.

like image 697
cervonwong Avatar asked Aug 12 '20 12:08

cervonwong


3 Answers

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<Student> students = new ArrayList<>();

    public Course() {
    }

    public Course (List<Student> 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<String> list = ...
Collections.sort(list);
list.add(...);

List<String> convert(List<String> 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).

like image 148
Joop Eggen Avatar answered Oct 17 '22 10:10

Joop Eggen


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

/// -- now...

Course history = new Course("History");
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.

like image 28
jbuddy Avatar answered Oct 17 '22 10:10

jbuddy


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

like image 5
ControlAltDel Avatar answered Oct 17 '22 09:10

ControlAltDel