【问题标题】:Interface method referencing a concrete class as parameter causes coupling?引用具体类作为参数的接口方法导致耦合?
【发布时间】:2014-04-14 11:07:51
【问题描述】:

我正在考虑对接口而不是具体类进行编程,但我有一个疑问:是否应该有任何接口方法能够保存对具体类的引用?

假设以下场景:

1)

public interface AbsType1 {
    public boolean method1(int a); // it's ok, only primitive types here
}

2)

public interface AbsType2 {
    public boolean method2(MyClass a); // I think I have some coupling here
}

我应该在这里选择不同的设计以避免后者吗?例如

public interface MyInterface {} // yes, this is empty

public classe MyClass implements MyInterface {
    // basically identical to the previous "MyClass"
}


public interface AbsType2 {
    public boolean method2(MyInterface a); // this is better (as long as the
                                           // interface is really stable)
}

但仍有一些事情不能说服我......我对声明一个空接口感到不舒服,尽管我看到其他人这样做。 也许抽象类在这里会更好用?

我有点困惑。

编辑:

好的,我将尝试通过举一个例子来更具体。假设我正在设计一个 ShopCart,我当然想将商品添加到购物车:

public interface ShopCart {
    public void addArticle(Article a);
}

现在,如果 Article 是一个具体的类,如果它的实现随时间发生变化怎么办?这就是为什么我可以考虑将其设为接口的原因,但话又说回来,它可能至少在语义级别上不合适,因为接口应该指定行为并且文章没有(或几乎没有......我猜它是一种实体类)。

所以,我现在可能最终得出的结论是,在这种情况下,将 Article 设为抽象类将是最好的事情......你怎么看?

【问题讨论】:

  • 我可以看到一切正常
  • 你为什么声明你的界面是空的? MyClass 中没有任何 AbsType 会关心的相关公共方法吗? - 如果没有,那么也许你最好对类类型使用泛型。

标签: java interface coupling loose-coupling concrete


【解决方案1】:

我会使用接口,因为组合比继承好得多。 “任何接口方法都应该能够保存对具体类的引用吗?”,为什么不应该呢?包中的一些类是耦合的,这是一个事实和常用的技术。当您在接口中标记此关系时,您会看到哪些类依赖于您的实现。依赖或组合关系不是继承,所以我会避免抽象类。

【讨论】:

  • 我添加了一个示例...在那种特定情况下,您也会避免使用抽象类吗?你会怎么做?
  • 我们也是这么想的,我也会把Article抽象化,这显然是继承关系,但是ShopCart指定了行为,操作在实体Article上,所以应该是接口。
【解决方案2】:

在我看来,Interfaces 适用于实现可能会有所不同的所有类型。但是,如果您定义了一个 module 引入了一种新类型,并且不打算有替代实现,那么首先就不需要将它定义为 Interface。在我看来,这通常是过度设计。这取决于问题域,并且通常取决于支持测试或 AOP 编织的方式。

例如考虑一个二维问题域,您需要将Location 建模为一个类型。如果很清楚 Location 始终由 xy 坐标表示,则可以将其提供为 Class。但是,如果您不知道 Location 可能具有哪些属性(GPS 数据、x、y、z 坐标等),但您依赖于 distance() 之类的某些行为,则应将其建模为接口。

【讨论】:

    【解决方案3】:

    如果AbsType 没有可以在MyClass 中访问的公共方法,那么空接口可能不是一个好方法。

    静态方法没有接口声明(契约),否则在这里可能有意义。

    所以,如果AbsType 不打算使用MyClass/MyInterface 中的任何方法,那么我认为它基本上只是为了其他目的而存储类对象。在这种情况下,请考虑使用泛型来明确您希望如何使用 AbsType 而无需与客户端代码紧密耦合,例如

    public class AbsType3<C extends Class<?>> {
    
      public boolean method3(T classType) {...}
    
    }
    

    然后,如果需要,您可以通过将 &lt;C extends Class&lt;?&gt;&gt; 类型参数交换为也可能是接口的其他东西来限制类的类型,例如 &lt;C extends Class&lt;Collection&lt;?&gt;&gt;&gt;.

    空接口有点像类的布尔标志:一个类要么实现接口(真),要么不实现(假)。如果有的话,应该使用这些标记接口来传达关于如何使用(或不使用)类的重要声明,例如,参见Serializable

    【讨论】:

    • 我提供了一个例子......在那种情况下,我认为没有必要使用泛型。你怎么看?
    • 你是对的。因此,如果您想实际使用传递给您的方法的对象,您可以选择接口、抽象类或具体类的任何方法。为了最大限度地减少耦合,我实际上建议使用接口。当然,接口不能像具体实现那样强制执行契约,但这不一定是问题,如果接口设计得仔细的话。 Otoh,只要您不在(抽象)基本实现中使用 final 方法,也无法强制执行合同;任何方法都可能被违反合同的行为覆盖。
    • 转到界面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-29
    • 2015-07-09
    • 1970-01-01
    • 2017-11-08
    • 1970-01-01
    • 2021-02-20
    • 2017-03-05
    相关资源
    最近更新 更多