我有两个不可变的类:用户和部门,它们使用双向关联连接 - 用户引用部门和部门有一个用户列表。如何使用提供的用户创建新的部门实例?
代码:
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;
}
}
答案 0 :(得分:5)
我觉得你可以稍微修改你的设计并使用特殊的UsersBuilder
,即
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);
}
}
通常,在构造函数完成之前使用object的引用并不是一个好主意;但在这种特殊情况下它看起来很安全。
在这种情况下这些对象将真正不可变。
答案 1 :(得分:1)
使用空列表用户实例化部门。然后使用Department实例化User并将用户实例添加到Department的用户列表中。
答案 2 :(得分:1)
一种方法是略微改变您理解 immutable 的含义。在面向对象的设计中,通常区分对象的属性及其关联。关联对象是对象具有引用的不同实体。如果放宽 immutable 的定义以表示对象的属性不会更改,但允许更改关联,则可以避免此类问题。
在您的情况下,User
和Department
个对象会彼此关联,每个对象都会有name
属性。
答案 3 :(得分:1)
您可以在Department上使用额外的构造函数生成不可变的Departments和Users。从问题&#39;代码,推断出
由于用户只是与部门关联的字符串,因此可以使用List<String>
构建部门,该部门代表要包含的所有用户名,并使用List<String>
创建List<User>
在部门构造函数内。
注意:关于让this
从构造函数中逃脱的@andremoniy said不应该成为习惯,但在这种情况下它是安全的,因为它只被传递给用户实例&#39;构造函数,在部门构造函数返回之前无法访问该用户实例。
这就是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;
}
}
此解决方案的一个问题是,一旦创建了Department实例,就可以将其添加到User的新实例,而不受使用更新的List创建Department的新实例的约束。考虑其他抽象或创建模式(如果您需要在保持不变性的同时支持从部门添加/删除用户,那么所有构造函数都是私有的,这是一个完全成熟的Builder实现)。
答案 4 :(得分:1)
我认为这也是建模的问题。可以认为用户有一个部门,一个部门有用户,但问题是你能从多大程度上查看用户和部门的数据?
除非您在概念上访问user.department.user [2] .name,否则它是否有意义?那么department.user [10] .addresses [1] .street?
在大多数情况下,我真的不这么认为。这是信息领域的问题。在访问数据时你有粘合剂,这也可以用某种方式表达到你的模型中。
如果对象建模类型代表现实世界,可以认为当你去一个部门时,你会看到很多人在那里工作,很可能你只能知道他们是计数和他们的名字也许。那么您应该从对象中看到哪些数据片?
我的方法是:
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();
// ...
}
我不认为我需要展示建设者的工作方式,因为如果您注意到,对于这种情况,关系的双向性从未出现过。因此,当您构建一个Person时,您所需要的只是DepartmentInfo(部门没有员工不需要),当您构建一个部门时,同样有效,当您需要拥有的是来自部门员工的PersonInfo时。 / p>
这是我从概念上思考这个问题的方法。有什么意见吗?
答案 5 :(得分:0)
我的解决方案是:将一个不可变类拆分为两个类:一个具有属性的类和一个具有双向关联的类:
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);
}
}
因此,要创建新用户和部门实例,您必须: