在Java中继承和封装集合类

时间:2009-07-01 10:36:32

标签: java inheritance collections encapsulation

假设我有以下类型的数据:

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等等)。任何指示?

6 个答案:

答案 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,它有一个标准的,明确定义的契约,但是你要避免扩展库类,这通常是不受欢迎的。

编辑:正如下面所指出的,这确实有一个缺点,就是要求你实现许多方法,这些方法不会对委托给底层集合做任何事情