Java: Объектно-ориентированный дизайн

Теория: Связь многие ко многим

Помимо связи один-ко-многим, очень распространена связь многие-ко-многим. Например, такой связью обладают курсы и пользователи. С одной стороны, пользователь может проходить множество курсов, с другой, курс проходится большим количеством студентов.

Кажется, что такую связь можно реализовать, сделав зависимую сущность коллекцией с обоих концов, со стороны курса и со стороны пользователя.

user.addCourse(course);
user.getCourses();
// или
course.addUser(user);
course.getUsers();

На практике же, такая реализация не позволяет смоделировать нашу предметную область правильно. Когда человек вступает в курс, то у него появляется дата вступления в курс, кроме этого, курс можно закончить, а это обязательно должно быть где-то отражено. Эта информация не относится ни к курсу, ни к пользователю, она относится к их связи.

Такую связь реализуют с помощью введения третьей сущности, которая может и, обычно, содержит какие-то дополнительные данные появляющиеся только во время создания связи. В случае курсов, мы точно хотим знать когда пользователь начал курс и когда он его закончил. Помимо этого мы можем добавить другую информацию, которая привязана именно к связи, а не к пользователю или курсу.

В случае курсов и пользователей, сущность, которая связывает курсы и пользователи можно назвать CourseMember (участник курса). Вот ее код:

import java.time.LocalDate;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class CourseMember {
    private Course course;
    private User user;

    private LocalDate startedAt;
    private LocalDate finishedAt;
}

И использование:

var course = new Course("Java ООП");
var user = new User("Mila");

var member = new CourseMember();
member.setCourse(course);
member.setUser(user);
member.setStartedAt(LocalDate.now());

Мы связали курсы и пользователей связью многие-ко-многим, при этом получившаяся сущность CourseMember связана с курсом связью один-ко-многим и с пользователем связью один-ко-многим. Визуально это выглядит так.

+----------------+ +---------------------+ +----------------+ | User | | CourseMember | | Course | +----------------+ +---------------------+ +----------------+ | - attributes |1 *| - Course |* 1| - attributes | | - methods |<---------| - User |--------->| - methods | +----------------+ | - startedAt | +----------------+ | - finishedAt | +---------------------+

Для полной реализации таких связей, нам нужно добавить CourseMember в каждую из зависимых сущностей.

@Setter
@Getter
class User {
    private String name;
    private List<CourseMember> courseMembers;

    public User(String name) {
        this.name = name;
        courseMembers = new ArrayList<>();
    }

    public void addCourseMember(CourseMember courseMember) {
        courseMembers.add(courseMember);
    }

    public void removeCourseMember(CourseMember courseMember) {
        courseMembers.remove(courseMember);
    }

    // другие методы
}

@Setter
@Getter
class Course {
    private String name;
    private List<CourseMember> courseMembers;

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

    public void addCourseMember(CourseMember courseMember) {
        courseMembers.add(courseMember);
    }

    public void removeCourseMember(CourseMember courseMember) {
        courseMembers.remove(courseMember);
    }

    // другие методы
}

@Getter
class CourseMember {
    private User user;
    private Course course;
    @Setter
    private LocalDate startedAt;
    @Setter
    private LocalDate finishedAt;

    public void setCourse(Course course) {
        this.course = course;
        course.addCourseMember(this);
    }

    public void setUser(User user) {
        this.user = user;
        user.addCourseMember(this);
    }
}

Рекомендуемые программы