我正在用 C#/WPF 编写一个相当大的应用程序。我使用Builder模式来创建始终处于一致状态的对象,并且对象是不可变的。
我在这个设计中遇到了一个问题,我不知道如何解决。
考虑以下两个类:
public class Employee {
public string Name { get; }
public double Salary { get; }
public IReadOnlyList<EmployeeBonus> Bonuses { get; } // read-only list of bonuses
public Employee(string name, double salary, IEnumerable<EmployeeBonus> bonuses) {
Name = name;
Salary = salary;
Bonuses = new List<EmployeeBonus>(bonuses); // list of bonuses initialized in constructor
}
}
public class EmployeeBonus {
public Employee Employee { get; } // bonus has reference to Employee
public string Description { get; }
public double Amount { get; }
public EmployeeBonus(Employee employee, string description, double amount) {
Employee = employee; // employee must be initialized in constructor
Description = description;
Amount = amount;
}
}
所以 - 我有一个 Employee 类,其中包含每个员工收到的奖金列表。 EmployeeBonus 类包含对 Employee 的引用。 由于这两个类都是不可变的,因此必须通过对另一个类的引用来初始化每个类。但这当然是不可能的,因为我无法创建一个引用另一个不存在的对象的对象。
我想到的解决方案:
A.我想过在EmployeeBonus中不要引用Employee,所以EmployeeBonus只需要构建Description和Amount。但这打破了我的存储库模式:我的 Repository<EmployeeBonus>
有一个仅接受 EmployeeBonus 的 Add 方法。为了正确保存该对象,我需要知道哪个员工拥有它,并且因为该方法仅接受 EmployeeBonus - 该对象必须包含 Employee。
B.我考虑过向 Employee 添加一个 AddBonus(string description, double amount) 方法,以便奖金列表不在构造函数中初始化,而是稍后添加每个奖金,并且 Employee 类将自身附加到每个奖金 - 但这会使 Employee 不再是一成不变的。
C.我可以破坏我的通用存储库并创建另一个方法,Add(EmployeBonus bonus, Employee employee)
,然后从 EmployeeBonus 中删除 Employee - 但这样我的 EmployeeBonusRepository 将不会继承 Repository<EmployeeBonus>
.
D.我能想到的最正确的解决方案(正确但非常浪费)是这样的:
public class Employee
{
public string Name { get; }
public double Salary { get; }
public IReadOnlyList<EmployeeBonus> Bonuses { get; }
public Employee(string name, double salary, IEnumerable<EmployeeBonus> bonuses) {
Name = name;
Salary = salary;
Bonuses = new List<EmployeeBonus>(bonuses);
}
}
public class EmployeeBonus
{
public string Description { get; }
public double Amount { get; }
public EmployeeBonus(string description, double amount) {
Description = description;
Amount = amount;
}
}
public class EmployeeBonusWithEmployee
{
public Employee Employee { get; }
public EmployeeBonus Bonus { get; }
public EmployeeBonusWithEmployee(Employee employee, EmployeeBonus bonus)
{
Employee = employee;
Bonus = bonus;
}
}
public class EmployeeBonusWithEmployeeRepository : Repository<EmployeeBonusWithEmployee>
{
public void Add(EmployeeBonusWithEmployee bonus)
{
// save complete employee bonus
}
}
public class EmployeeRepository : Repository<Employee>
{
//...
public void Add(Employee employee)
{
// saves employee first,
// then creates an EmployeeBonusWithEmployee object for each EmployeeBonus in the list
// and saves it using an EmployeeBonusWithEmployeeRepository
}
}
这个解决方案 (D) 有意义吗?有更优雅的方法吗?
最佳答案
要在 C# 中创建相互引用的对象,您需要延迟执行相互引用的代码 - 直到两个对象都已创建。
public class B
{
private Lazy<A> _a;
public A GetA
{
get { return _a.Value; }
}
public B(Lazy<A> forLater)
{
_a = forLater;
}
}
与 A 类相同。 然后创建相互引用的对象:
A a = null;
B b = null;
a = new A(new Lazy<B>(() => b));
b = new B(new Lazy<A>(() => a));
Lazy<T>将代码的执行延迟到稍后,从而允许两个构造函数完成。
这是一个糟糕的解决方案 - 其他语言使这变得更容易 - 所以我建议一个更实用的方法。
- 使它们可变,或者
- 将员工从员工奖金中剔除
关于C# 设计问题 : two immutable objects with references to each other,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49304616/