【问题标题】:Should I be using interfaces like this?我应该使用这样的接口吗?
【发布时间】:2010-11-30 20:49:41
【问题描述】:

我发现自己处于一种我想使用接口的情况,但我又在猜测自己。

我正在编写一个应用程序来处理保险申请。有一个 Application 对象保存有关保险申请的所有数据。这包括有关被保险人、将拥有保险单的实体以及将支付保险费用的实体的数据。

我使用实体一词是因为保单可以由个人或信托拥有(或支付)。我知道我何时与所有者或付款人打交道,但我不一定知道该所有者或付款人是个人还是信托。我最初创建了 Owner 接口来减少强制转换和基于实例的逻辑。我计划在 Payor 界面上装订,但现在我在怀疑自己。

这可能是您现在需要知道的全部内容。这是一些代码:

public class Application{
    private Person insured;
    private Owner owner;
    private Payor payor;

    ...

}

public interface Owner{
    public void setAddress(Address address);
    public Address getAddress();

    public void setId(String id);
    public String getId();

    public void setPIN(String pin);
    public String getPIN();
}

public interface Payor{
    public void setAccount(Account account);
    public Account getAccount();
}

public class Person implements Owner, Payor{
    ...
}

public class Trust implements Owner, Payor{
    ...
}

我是在正确的道路上还是应该以不同的方式做这件事?让我停下来的事情是,并非每个人都会成为所有者或付款人。

当我想得更多时,我觉得 Owner 和 Payor 与其说是“行为”,不如说是“分类”。这是否使我在这里使用的接口不正确?如果是这样,您对替代解决方案有什么建议吗?最好是那些允许我继续透明地使用个人和信托作为所有者和付款人的那些?

我的部分担忧来自不太熟悉的开发人员将 Person 与 Owner 混淆,如下所示。

Application app = new Application();
Person timmy = new Person();
Owner smithFamilyTrust = new Trust();
Payor steve = new Person();

app.setInsured(timmy);
app.setOwner(smithFamilyTrust);
app.setPayor(steve);

...

//this would run, but would be wrong
if(timmy instanceof Owner){
   //Do Owner Stuff.
}

//this would run, and be correct
Owner owner = app.getOwner();
//Do Owner Stuff

编辑以澄清“所有者资料”

此时,“所有者的东西”很简单。获取/设置所有者的 ID、PIN 或地址之类的事情:

//I want this
app.getOwner().getId();
app.getOwner().getPIN();
app.getOwner().getAddress();

//I don't want this
if(app.getOwner() instanceof Person){
    Person owner = app.getOwner();
    owner.getId();
    owner.getPIN();
    owner.getAddress();
} else if(app.getOwner() instanceof Trust){
    Trust owner = app.getOwner();
    owner.getId();
    owner.getPIN();
    owner.getAddress();
}

最初我认为我应该使用某种 Entity 超类并让 Person 和 Trust 扩展该类,但它们存储和检索 ID 和 PIN 的方式不同。相同行为的不同实现使我想到了接口。

我仍然可以使用实体超类,但我觉得我无法准确地表示该实体类的“通用”ID 或 PIN。我必须实现 Person 的 ID 和 PIN 逻辑或 Trust 的 ID 和 PIN 逻辑,然后在另一个类中覆盖它。这对我来说感觉不对。我想 Entity 可能是 Person 和 Trust 扩展的抽象类,但我不确定这是否比使用接口更好。

【问题讨论】:

  • 你什么时候使用timmy instanceof Owner,你会做什么“所有者的东西”?在我看来,实体是所有者还是付款人仅在特定应用程序的上下文中定义,您已经知道哪个是哪个。
  • 类型系统在定义你所说的那种语义方面可能有点弱。传达这种隐性信息通常是通过其他方式完成的,选择结对编程、综合测试/规范套件中的任何一种,包括如何使用每个类的示例或老式的 powerpoint 会话。
  • @Dmitri "owner stuff" 在这一点上很简单,比如获取邮寄地址、税号或 PIN。所以,我想做的是 app.getOwner().getAddress() 而不必确定所有者是个人还是信托。

标签: java oop inheritance interface


【解决方案1】:

以这种方式使用接口可能合适,也可能不合适。如果您是第二次猜测自己,那么您可能介绍得太早了。

接口的实际目的是允许一段代码针对多个实现运行,例如

interface Contactable
  Address getAddress()
end

class Person implements Contactable
  Address getAddress(){
    ...
  }
end

class Company implements Contactable
  Address getAddress() {
    ...
  }
end

class RenewalReminderJob extends CronJob {
  public void perform() {
    for(Contactable upForRenewal : listOfCompaniesAndPeople) {
      remind(upForRenewal)
    }
  }
}

通常,当您发现要对包含相同信息的不同类执行操作时,最好从具体类开始,然后引入接口(使用重构)。在上面的示例中,它将是地址。

随着代码库的开发,主动搜索接口的过程非常强大,它可以让您发现可能不会立即显而易见的概念。

【讨论】:

  • 我喜欢这个答案,仅仅是因为它赞同构建可能可行的最简单事物的概念。在基本层面实现这个想法可以简化增加复杂性的工作。
  • 我认为这是对何时使用接口的一个很好的描述,它让我想知道我的接口是否过于宽泛。我可以将它从所有者接口更改为可接触和可识别的接口,这更多地遵循接口命名约定,但是我不知道我会在应用程序中使用什么来存储所有者。 Contactable owner 不起作用,因为如果不进行强制转换,我将无法从中获取 ID 或 PIN。
  • 是的,我认为你完全正确,像那些更小的接口可以更容易地重用。我经常使用的一个接口是 Labeled,它只有一个方法 getLabel()。我发现这对前端非常有用,因为这意味着我可以编写可以在我的 DOM 中显示任何对象的代码/组件,我只需要实现一个简单的方法。
【解决方案2】:

正确的实现方式是这样的 应用程序具有“'角色'实体”(即实体在应用程序中扮演不同的角色)

实体可以是个人/组织/受托人等

因此,如果您以这种方式构建您的课程,您将不会遇到问题。

作为一个例子,我可以有应用程序

             entity1 (insured )       - person 
             entity2 (payer )         - person 
             entity3 (beneficiary)    - trustee
             entity4 (broker)         - organisation

【讨论】:

    【解决方案3】:

    你回答了你自己的问题

    我是在正确的道路上还是应该 以不同的方式做这件事?事情 这让我停下来是事实 不是每个人都会成为所有者 或付款人。

    越想越觉得 所有者和付款人不是“行为”所以 就像“分类”一样。这样做 在这里使用接口 不正确?

    接口用于定义行为。

    您是否尝试过子类方法?

    【讨论】:

      【解决方案4】:

      “所有者和付款人与其说是行为,不如说是分类。”

      是否有与之相关的行为?例如,您可能需要一个方法

      sendInvoice(Payor payor)
      

      Payor getAddressForInvoice()
      

      如果是这样,那么您可能需要将它们作为接口,尤其是当它们可能是个人或公司时。

      【讨论】:

        【解决方案5】:

        您可以做的一件事是从继承切换到包含。与其说“个人或信托可以是所有者或付款人”,不如说“个人和信托可以包含所有者或付款人”:

        public class Person {
            public Owner getOwner() { /* will return null if this person is not an Owner */ }
            public Payor getPayor() { /* will return null if this person is not an Payor */ }
        }
        
        public class Trust {
            public Owner getOwner() { /* will return null if this trust is not an Owner */ }
            public Payor getPayor() { /* will return null if this trust is not an Payor */ }
        }
        

        不过,我不确定这是否是正确答案,因为我不确定“所有者”和“付款人”分类背后的业务逻辑。如果一个人是所有者,这是否意味着他可以拥有任何应用程序?或者,相反,这是否仅仅意味着这个特定的 Person 拥有这个特定的 Application ?如果后者为真,那么您可以从一个公共基础接口(例如“LegalEntity”)扩展 Person 和 Trust,并使用 Visitor pattern 而不是 instanceof 检查来提供特定于子类的行为。

        【讨论】:

          【解决方案6】:

          您的界面没有任何问题。 Owner 界面只是意味着能够拥有东西,而且,除非你想对可怜的 Timmy 极度不公平,否则我看不出有什么理由阻止他拥有。

          关于您的担忧,我认为您的保护有点过头了:您的系统中的任何时候都会有多个 Owner 实例,我看不到任何潜在的开发人员假设他们可以选择任何一个一个并将其视为特定应用程序的所有者。

          【讨论】:

          • 这是我希望看到的答案。我认为你是对的,我有点过度保护,但我讨厌让这样的东西敞开。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-27
          • 2021-05-24
          • 1970-01-01
          • 2011-07-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多