如何在双向关联中实例化不可变类?

Ada*_*ion 7 java software-design immutability

我有两个不可变的类:用户部门,它们使用双向关联连接 - 用户部门的引用,部门有一个用户列表.如何使用提供的用户创建新的Department实例?

码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

And*_*niy 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);
    }    
}
Run Code Online (Sandbox Code Playgroud)

通常,在构造函数完成之前使用object的引用并不是一个好主意; 但在这种特殊情况下它看起来很安全.

在这种情况下,这些对象将是真正不可变的.


wha*_*ley 1

您可以使用部门上的额外构造函数来生成不可变的部门和用户。从问题代码可以推断

  • User 对象只是 String 和 Department 之间的关联
  • 如果没有部门参考,用户参考就不可能存在。

由于用户实际上只是与部门关联的字符串,因此可以使用表示List<String>要包含的所有用户名的 来构造部门,并使用它在部门构造函数中 List<String>创建一个。List<User>

注意:@andremoniy 所说的让this从构造函数转义不应该成为一种习惯,但在这种情况下它是安全的,因为它仅被传递给 User 实例的构造函数,其中该 User 实例无法在 Department 之前访问构造函数返回。

在 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

该解决方案存在的一个问题是,一旦创建了 Department 实例,就可以将其添加到 User 的新实例中,而无需使用更新的 List 创建新的 Department 实例。如果您需要支持在部门中添加/删除用户,同时保持不变性,请考虑其他抽象或创建模式(所有构造函数均为私有的完整构建器实现在这里将是一个很好的匹配)。