5

我试图了解好的合同在哪里结束,而偏执狂从哪里开始。真的,我只是不知道优秀的开发人员应该关心什么以及他应该忽略什么:)

假设我有一个保存值的类,比如 java.lang.Integer。它的实例由其他对象(MappedObjects)聚合(一对多或多对多),并且经常在 MappedObjects 的方法中使用。此外,出于性能原因,我还在 TreeMap(guava MultiMap,没关系)中跟踪这些关系,以便能够对绑定到某个整数键范围的 MappedObjects 进行快速迭代。因此,为了使系统保持一致状态,我应该修改 MappedObject.bind(Integer integer) 方法来更新我的 Map,如:

class MappedObject {
    public void bind (Integer integer) {
        MegaMap.getInstance().remove(fInteger, this);
        fInteger = integer;
        MegaMap.getInstance().add(fInteger, this);
    }

    ...

    private Integer fInteger;
}

我可以用这个 final 方法创建抽象的 MappedObject 类,强迫其他人从它继承,但这很粗鲁。如果我将 MappedObject 定义为带有方法 bind() 的接口并提供骨架实现——其他开发人员稍后可能会忘记将其包含在对象中并在不更新 Map 的情况下自行实现方法。

4

5 回答 5

4

是的,你应该强迫人们用你的代码做正确的事情。让人们做错事的一个很好的例子是 servlet 方法init( ServletConfig config ),它期望您自己存储 servlet 配置,但显然,很多人忘记存储配置,并且在运行他们的 servlet 时无法正常工作.

在定义 API 时,应始终遵循开闭原则,您的类应该对扩展开放,对修改关闭。如果你的类必须像这样工作,你应该只在有意义的地方打开扩展点,所有其他功能不应该可用于修改,因为它可能会导致未来的实现问题。

于 2011-11-29T13:09:53.007 回答
1

试着首先关注功能,把所有不必要的东西都抛在脑后。顺便说一句,你不能禁止反思,所以不要太担心误用。另一方面,你的 API 应该清晰直接,这样用户就会清楚地知道他们应该做什么,不应该做什么。

于 2011-11-29T13:05:22.420 回答
1

至少你应该做所有可以防止错误但不花费任何努力的事情。

例如:如果变量不允许为空,则使用原始类型 ( int) 而不是包装器 ( )。Integer

所以在你的bind方法中。如果您不打算绑定null,则使用int而不是Integer作为参数类型。

于 2011-11-29T13:06:06.347 回答
1

我想说你的课程应该设计得尽可能简单。

如果您允许开发人员覆盖方法,您绝对应该尽可能好地记录合同。在这种情况下,开发人员选择覆盖一些基本功能,因此负责提供符合合同的实现。

如果您不希望开发人员覆盖部分功能 - 出于安全原因,如果没有合理的替代方案等 - 只需将该部分设为最终版本。在您的情况下,绑定方法可能如下所示:

class MappedObject {
public final void bind (Integer integer) {
    MegaMap.getInstance().remove(fInteger);
    internalBind( integer );
    MegaMap.getInstance().add(fInteger);
}

protected void internalBind( Integer integer ) {
 fInteger = integer;
}

...

private Integer fInteger;

}

在这里,您将允许开发人员覆盖该internalBind()方法,但确保bind()将执行映射。

总结一下:尽可能(明智地)使用和扩展类,并且不要让开发人员复制大量样板代码(例如您的案例中的地图更新),以防他只想覆盖一些基本功能(例如实际绑定)。

于 2011-11-29T13:10:00.463 回答
0

如果你认为你的 API 用户很愚蠢,你应该禁止错误的使用。否则,您不应该妨碍他们做他们需要做的事情。

类和方法的文档和良好命名应表明如何使用您的 API。

于 2011-11-29T13:09:49.547 回答