使用域对象作为键是不是很好的做法?

2022-09-03 01:53:47

是将域对象用作映射(或“get”方法)的键是好的做法,还是仅使用域对象的 id 更好?

用一个例子来解释会更简单。假设我有Person类,一个俱乐部类和一个会员类(连接其他两个)。即

public class Person {
    private int id; // primary key
    private String name;
}

public class Club {
    private String name; // primary key
}

public class Membership {
    private Person person;
    private Club club;
    private Date expires;
}

或类似的东西。现在,我想向俱乐部添加一个方法。问题是,此方法是否应采用 Person 对象:getMembership

public Membership getMembership(Person person);

或者,一个人的ID:

public Membership getMembership(int personId);

哪个是最惯用语,哪个最方便,哪个最合适?

编辑:许多非常好的答案。我没有公开ID,因为“Person”(您可能已经意识到,我的真实域与人和俱乐部没有任何关系......)实例很容易获得,但现在它存储在Id上散列的HashMap中 - 但至少我在界面中正确公开了它。


答案 1

不要使用id的男人,由于上述所有原因,这只是一个坏主意。您将将自己锁定在设计中。让我举个例子。

现在,您将会员资格定义为俱乐部与人员之间的映射。理所当然,您的会员资格应该是俱乐部到“会员”的地图,但您假设所有会员都是人,并且由于所有的人ID都是唯一的,因此您认为您可以使用ID。

但是,如果将来要将成员资格概念扩展到“家庭成员资格”,并为其创建一个“家庭”表和一个“家庭”类,该怎么办?在良好的OO方式中,您可以提取称为成员的家庭和人员的界面。只要两个类都正确实现了 equals 和 hashCode 方法,就不必触及其他代码。就个人而言,我会预先定义成员界面。

public interface Member {
}

public class Person implements Member {
    private int id; // primary key
    private String name;
}

public class Family implements Member {
   private int id;
   private String name;
}

public class Club {
    private String name; // primary key
}

public class Membership {
   private Member member;
   private Club club;
   private Date expires;
}

如果您在界面中使用了ID,则需要强制实施键值的跨表唯一性,或者维护两个单独的Maps并放弃漂亮的多态界面内容。

相信我,除非你正在编写一次性的一次性应用程序,否则你要避免在界面中使用ID。


答案 2

假设这是一个数据库ID或仅用于索引的东西(而不是像SSN这样的东西),那么在理想的系统中,ID的存在是一个实现细节。

作为实现细节,我更喜欢将其隐藏在其他域对象的接口中。因此,从根本上说,成员资格涉及个人而不是数字。

当然,我会确保我实现并很好地记录了它们的含义。hashCodeequals()

在这种情况下,我会明确记录两个 Person 对象的相等性仅基于 ID 确定。这是一个有点冒险的提议,但如果你能确保它,使代码更具可读性。当我的对象不可变时,我感觉更舒服,所以我实际上不会在程序的生命周期中得到两个具有相同ID但名称不同的Person对象。