Peter Meyer的“编程到界面”答案的后续行动

时间:2013-05-09 17:08:09

标签: oop language-agnostic interface

这是@Peter Meyer在这个问题(What does it mean to "program to an interface"?)中给出的答案的后续问题。

首先,让我首先说我不愿意将这个问题作为一个新问题。但是(我喜欢stackoverflow,但我必须在这里有点批评)1)我不能私下给Peter Meyer留言(阅读:https://meta.stackexchange.com/questions/93896/a-proposal-for-private-messaging-within-the-stack-exchange-network),2)我无法发布'后续'问题(阅读:{ {3}})和3),问题被锁定以避免“爆炸药”,唉,我没有足够的声誉在那里问。

所以,我必须发一个新问题。

在那个帖子中,Peter Meyer展示了一个非常有趣的例子,说明何时使用接口以及为什么编程接口非常重要。

我的问题是:不会使用包装类来解决他的问题吗?

你不能写:

interface IPest {
   void BeAnnoying();
}


class HouseFly inherits Insect implements IPest {
  void FlyAroundYourHead();
  void LandOnThings();

  void BeAnnoying() {
    FlyAroundYourHead();
    LandOnThings();
  }
}

class Telemarketer inherits Person implements IPest {
  void CallDuringDinner();
  void ContinueTalkingWhenYouSayNo();

  void BeAnnoying() {
     CallDuringDinner();
     ContinueTalkingWhenYouSayNo();
  }
}
class DiningRoom {

  DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

  void ServeDinner() {
    when diningPeople are eating,

      foreach pest in pests
        pest.BeAnnoying();
  }
}

以这种方式:

class IPest {
  HouseFly houseFly;
  public IPest(HouseFly houseFly) {
    this.houseFly = houseFly;
  }
  Telemarketer telemarketer;
  public IPest(Telemarketer telemarketer) {
    this.telemarketer = telemarketer;
  }
  void BeAnnoying() {
    if(houseFly != null)
      houseFly.BeAnnoying();
    else
      telemarketer.BeAnnoying();
  }
}


class HouseFly inherits Insect {
  void FlyAroundYourHead();
  void LandOnThings();

  void BeAnnoying() {
    FlyAroundYourHead();
    LandOnThings();
  }
}

class Telemarketer inherits Person {
  void CallDuringDinner();
  void ContinueTalkingWhenYouSayNo();

  void BeAnnoying() {
     CallDuringDinner();
     ContinueTalkingWhenYouSayNo();
  }
}
class DiningRoom {

  DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

  void ServeDinner() {
    when diningPeople are eating,
      foreach pest in pests
        pest.BeAnnoying();
  }
}

虽然我将此标记为与语言无关,但我真的是“Java-ifying”这个问题,因为这是我最熟悉的,所以请原谅我。但正如我所看到的,使用接口方法存在缺点。例如,如果要覆盖不同类型的“toString()”方法,则根据它是否表示为“IPest”或“HouseFly”来返回不同的值,使用该界面无法执行此操作。您不能为“HouseFly”提供不同的toString值,而不是HouseFly为接口实现IPest接口(因为HouseFly将始终通过类定义实现接口)。包装类将为您提供比界面更广泛的功能。

举例说明:假设您想要在列表中显示所有“IPest”,但您希望列表在每个列表上都有一个区别标记,以显示该害虫是Fly还是Telemarketer。然后使用包装器类,这很容易:

class IPest {
  HouseFly houseFly;
  public IPest(HouseFly houseFly) {
    this.houseFly = houseFly;
  }
  Telemarketer telemarketer;
  public IPest(Telemarketer telemarketer) {
    this.telemarketer = telemarketer;
  }
  void BeAnnoying() {
    if(houseFly != null)
      houseFly.BeAnnoying();
    else
      telemarketer.BeAnnoying();
  }
  public String toString() {
    return (houseFly == null? "(T) " + telemarketer.toString() : "(F) " + houseFly.toString()) + 
  }
}

然后,在另一个地方,如果你有一个列表来表示HouseFly本身(不是作为IPest而是作为HouseFly),那么你可以为toString()提供不同的值。

这不仅限于toString(),而是这些类可能具有的任何其他方法,当对象被表示为IPest与表示为HouseFly时,您可能希望覆盖这些方法以提供不同的功能或电话推销员。

我希望我的问题有道理。

我的理论是:如果您正在编程API或任何人将使用的任何东西,您应该避免使用具体的类并尝试使用接口。但是,如果您直接编写客户端代码并且对代码重用没有期望(或可能性),那么“编程到接口”似乎不是那么大。

我期待着任何反馈。我在这里偏离基地吗?我编写代码时很糟糕吗?希望Peter Meyer能够提供他的意见......

1 个答案:

答案 0 :(得分:3)

对我来说,这使代码变得更加丑陋,没有明显的好处。 (那听起来很严厉,这不是我想要的......实际问题是+ 1)。

最大的缺点是你的IPest课程。当你继续添加可能的害虫时,这个类会因未使用的变量和代码而变得庞大而臃肿。如果您有30种不同类型的害虫,那么您的IPest类增长了15倍,比您的示例有2种,需要大量额外代码来支持所有这些类。

更糟糕的是,绝大多数此代码实际上与实例化对象无关。如果IPest应该代表HouseFly,那么有几个实例变量(每个其他类型的IPest一个)都是空的,以及大量未使用的代码。更糟糕的是,如果IPest的多个值不为空,会发生什么?它是什么? (BrundleFly电话推销员!)

将它与纯接口进行比较,随着更多类的实现,它不再增长(因为它不关心。)

最后,我认为只有两个(或更多)对象(例如HouseFly对象和IPest对象)表示的单个概念性概念(例如单个苍蝇)通常是有用的(或者一个好主意)。 。更多对象,因为您想要添加更多功能。对于每个包装器,您添加另一个对象,这是另一个可能跟踪和更新的对象。

这并不是说不可能有一些非常专业的案例,这样的事情不是一个好主意......但我在这里没有看到它,原因我在上面描述。