How to create unique classes in a bi-directional association?

I have two immutable classes: User and Department , they are associated with the use of bidirectional communication - the User has a link to the Department and the Department has a list of Users . How to create a new department with the provided Users ?

the code:

class User { private final Department department; private final String name; public User(Department department, String name) { this.department = department; this.name = name; } } class Department { private final List<User> users; private final String name; public Department(List<User> users, String name) { this.users = new ArrayList<>(users); this.name = name; } } 
+5
source share
6 answers

You can create immutable departments and users with an additional constructor in the department. From the code of questions it is deduced that

  • A custom object is just an association between a String and a department
  • Custom links cannot exist without a department link.

Because users are truly just strings associated with a department, a department can be built with a List<String> , which represents all the usernames to be included, and use this List<String> to create a List<User> in the Departmentโ€™s constructor.

Note: what @andremoniy said about letting this exit the constructor should not be familiar, but in this case it is safe, as it is only passed to the constructor of the user instance, where this user instance cannot be accessed before the constructor department will return.

Here's what it would look like in Java 8:

 public final class User { private final Department department; private final String name; public User(Department department, String name) { this.department = department; this.name = name; } public Department getDepartment() { return department; } public String getName() { return name; } } public final class Department { private final List<User> users; private final String name; ///Reversed argument list to avoid collision after erasure public Department(String name, List<String> users) { this.users = Collections.unmodifiableList(users.stream() .map((s) -> new User(this,s)).collect(Collectors.toList())); this.name = name; } public Department(List<User> users, String name) { this.users = Collections.unmodifiableList(users); this.name = name; } public List<User> getUsers() { return users; } public String getName() { return name; } } 

One problem is that after creating a department instance, it can be added to new user instances without limiting that a new department instance will be created with an updated list. Consider other abstractions or creation patterns (a full-blown Builder implementation where all the constructors are private will be a good match here) if you need to support adding / removing users from the department while maintaining immutability.

+1
source

In your case, I feel that you can slightly change your design and use special UsersBuilder , i.e.

 class Department { final List<User> users; final String name; public Department(String name) { this.users = UsersBuilder.buildUsers(this); this.name = name; } } class UsersBuilder { public static List<User> buildUsers(Department department) { List<User> usersList = new ArrayList<>(); // add users to the list via department reference return Collections.unmodifiableList(usersList); } } 

In general, it is actually not recommended to use the reference to the object until the completion of its constructor; but in this particular case it looks safe.

In this case, these objects will be really immutable.

+5
source

Create a department with an empty user list. Then use the Department to create a user instance and add the user instance to the Department's user list.

+1
source

One approach is to slightly change what you understand as unchanging. In an object-oriented design, it is customary to distinguish between the attributes of an object and its associations. Linked objects are different objects to which the object has links. If you loosen the definition of immutability to mean that the attributes of an object do not change, but allow associations to change, you avoid this problem.

In your case, the User and Department objects will be related to each other, and each of them will have a name attribute.

+1
source

I think this is also a matter of modeling. Itโ€™s normal to think that the User has a Department and the Department has Users, but the question is how deep can you look at the data from the user and the department?

Does it make sense if conceptually you do not get access to user.department.user [2] .name? What about department.user [10] .addresses [1] .street?

I really don't think about it in most scenarios. This is an information domain question. You have bonds when accessing data, and it can also be somehow expressed in your models.

If the Object Modeling object represents the real world, itโ€™s normal to think that when you go to the department, you will see that dozens of people work there, and most likely all you can find out about them is the count and their names, maybe . So, what pieces of data should you see from your object?

My approach for this is:

 interface PersonInfo { String name(); String lastName(); default fullName() { return name() + " " + lastName(); } static PersonInfoBuilder personInfo() { return new PersonInfoBuilder(); } static class PersonInfoBuilder { ... } } interface Person extends PersonInfo { DepartmentInfo department(); Set<Address> addresses(); //... } interface DepartmentInfo { String name(); String building(); // builder ... } interface Department extends DepartmentInfo { Set<PersonInfo> employees(); // ... } 

I donโ€™t think that I will need to show how the builders will work, because if you noticed, for this scenario, the bidirectional nature of the relationship will never be. Therefore, when you create a Person, all you need is DepartmentInfo (the department does not need employees), and this is true when creating a department, when all you need is PersonInfo from the employees of the department.

This is my way of thinking about this problem conceptually. Any comments?

+1
source

My solution is this: divide one of the immutable classes into two classes: a class with attributes and a class with bi-directional association:

 class Department { private final String name; public Department(String name) { this.name = name; } public String getName() { return name; } } class User { private final Department department; private final String name; public User(Department department, String name) { this.department = department; this.name = name; } } class DepartmentWithUsers { private final List<User> users; private final Department department; public DepartmentWithUsers(Department department, List<User> users) { this.department = department; this.users = new ArrayList<>(users); } } 

So, to create a new user and department instance, you must:

  • create a new instance of the department.
  • create a new user instance and transfer the created department instance
  • create a new instance of DepartmentWithUsers and transfer the created instance of the user
0
source

All Articles