假设我有以下类型的数据:
class Customer {
String id; // unique
OtherCustData someOtherData;
}
class Service {
String url; // unique
OtherServiceData someOtherData;
}
class LastConnection {
Date date;
OtherConnData someOtherData; // like request or response
}
现在我需要记住每个客户连接到每个服务的时间 我会做结构:
Map<Customer, Map<Service, LastConnection>> lastConnections;
或者,能够通过id搜索而不必编写所有的equal()和hashCode():
Map<String, Map<String, LastConnection>> lastConnections;
现在我可以通过
访问LastConnection数据了LastConnection connection = lastConnections.get(custId).get(srvUrl);
这一切看起来都很难看,尤其是我必须将它作为参数传递给数十种期望LastConnections地图的方法,所以我想创建自己的类看起来像这样:
class CustomerConnections extends HashMap<String, LastConnection> {
}
class AllConnections extends HashMap<String, CustomerConnections> {
public LastConnection get(String custId, String srvUrl) {
return get(custId).get(srvUrl);
}
}
好的,我已经知道继承是3v1l,所以让我们尝试组合:
class CustomerConnections {
Map<String, LastConnection> customerConnections;
LastConnection get(String srvUrl) {
return customerConnections.get(srvUrl);
}
... // all other needed operations;
}
class AllConnections {
Map<String, CustomerConnections> allConnections;
public LastConnection get(String custId, String srvUrl) {
return get(custId).get(srvUrl);
}
public CustomerConnection get(String custId) {
return allConnections.get(custId);
}
... // all other needed operations;
}
问题是我不确定什么是尊重SOLID原则和所有最佳实践的最佳方法。创建除了扩展现有集合之外什么都不做的类似乎会使实体成倍增加,但会使我的代码更加清晰(特别是当有下一个级别时 - 比如按月映射AllConnections等等)。任何指示?
答案 0 :(得分:5)
创建不执行任何操作的类 除了扩展已存在 收藏似乎成倍增加 超出必要的实体
我会将extend更改为encapsulate。您正在隐藏有关如何存储此信息的详细信息。您班级的客户无需了解您如何向他们提供客户连接历史记录。我认为这是一个好主意,因为你可以在不让api的客户改变代码的情况下更改底层模型。
但会使我的代码更清晰
这很棒,这是一个很好的理由。 YourClass.getCustomerConnection(cId)比yourCollection.get(id).get(id).getConnection()更清晰。即使你是那个人,你也需要让使用这个代码的人的生活更轻松。
(特别是当有下一个级别时 - 比如按月的AllConnections地图等等)
很好,那么您正在提前计划并使您的代码可扩展。哪个是好的OO练习。在我看来,你自己达成的结论是我会做的。
答案 1 :(得分:1)
我会创建一个专用对象来存储这些信息。您正在创建的是经理对象,而不是简单的集合。
我不会从Map或其他众所周知的集合类派生它,因为存储此信息的语义可能在将来发生变化。
而是实现一个将客户及其连接连接在一起的类,并在该类中使用适当的集合类(您可以在以后更改而不影响接口和其余代码)
您的客户/连接管理器类不仅仅是一个简单的容器。它可以存储元数据(例如,何时建立这种关系)。它可以在给定客户信息的连接上执行搜索(如果需要)。它可以处理您需要的重复项,而不是底层集合类如何处理它们等。您可以轻松插入调试/日志记录/性能监视,以便轻松了解正在发生的事情。
答案 2 :(得分:1)
我认为您还应该考虑如何使用集合以及如何获取数据:
如何获取并保留您的数据?如果它存储在数据库中,那么您可以使用SQL按客户和/或服务和/或月份选择LastConnections,而不必自己维护数据结构(只返回简单列表或连接映射或连接数) )。或者您可能不希望为每个请求运行查询,因此需要将整个数据结构保留在内存中。
封装通常是一件好事,特别是因为它可以帮助你坚持Law of Demeter - 也许你可以将你将在集合上执行的一些操作推回到AllConnections类(这是实际上是一个DAO)。这通常有助于单元测试。
另外,为什么在这里扩展HashMap被认为是邪恶的,考虑到你只想添加一个简单的辅助方法?在AllConnections的代码中,AllConnections将始终以与HashMap相同的方式运行 - 它是多态可替换的。当然,您可能会锁定自己使用HashMap(而不是TreeMap等),但这可能无关紧要,因为它具有与Map相同的公共方法。但是,你是否真的想要这样做真的取决于你打算如何使用这些集合 - 我只是不认为你应该自动折扣实现继承,因为总是坏(它通常只是!)
class AllConnections extends HashMap<String, CustomerConnections> {
public LastConnection get(String custId, String srvUrl) {
return get(custId).get(srvUrl);
}
}
答案 3 :(得分:0)
为什么不在简单的custId+"#"+srvurl
中使用Map<String, LastConnection>
作为密钥?
或者使用Tuple或Pair类作为包含两个ID的密钥,并实现hashCode()
和equals()
- “最干净”的OO解决方案。
答案 4 :(得分:0)
为什么要维护这些对象之外的对象之间的关系。
我建议如下:
public class Customer
{
public List<LastConnection> getConnectionHistory()
{
...
}
public List<LastConnection> getConnectionHistory(Service service)
{
...
}
public List<LastConnection> getConnectionHistory(Date since)
{
...
}
}
public class LastConnection
{
public Date getConnectionTime()
{
...
}
public Service getService()
{
...
}
}
然后将List<Customer>
传递给使用此信息的方法。
答案 5 :(得分:-1)
您可以创建一个实现 Map<K,V>
的类,并在内部委托给容器地图:
class CustomerConnections implements Map<String,LastConnection> {
private Map<String, LastConnection> customerConnections;
@Override
public LastConnection get(Object srvUrl) {
return customerConnections.get(srvUrl);
}
// all other needed operations;
}
这种方法的好处在于你可以传递一个Map
,它有一个标准的,明确定义的契约,但是你要避免扩展库类,这通常是不受欢迎的。
编辑:正如下面所指出的,这确实有一个缺点,就是要求你实现许多方法,这些方法不会对委托给底层集合做任何事情