3

This is a follow-up question to the answer given by @Peter Meyer given in this question (What does it mean to "program to an interface"?).

First, let me begin by saying I am loath to make this a new question. But (I love stackoverflow but I must be a little critical here) 1) I could not privately message Peter Meyer (read: https://meta.stackexchange.com/questions/93896/a-proposal-for-private-messaging-within-the-stack-exchange-network), 2) I could not post a 'follow-up' question (read: https://meta.stackexchange.com/questions/10243/asking-a-follow-up-question) and 3), the question was locked to avoid "explosion pills" and, alas, I do not have enough reputation to ask there.

So, I must post a new question.

In that thread, Peter Meyer showed a brilliant and funny example of when to use interfaces and why programming to interfaces is important.

My question is this: wouldn't using a wrapper class be another approach to solving his problem?

Couldn't you write:

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();
  }
}

in this way:

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();
  }
}

?

Although I tagged this as language-agnostic, I'm really "Java-ifying" this question because that is what I'm most familiar with, so please forgive me. But as I see it, there is disadvantage to using the interface approach. For instance, if you want to override the "toString()" method for the different types, to return a different value based on whether it's being represented as an "IPest" or a "HouseFly," with the interface you cannot do that. You can't give a different toString value for "HouseFly" by itself than you would for a HouseFly implementing the IPest interface with the interface (because HouseFly will always implement the interface by the class definition). A wrapper class would give you broader functionality than the interface would.

To illustrate: Let's say you wanted to display all of the "IPests" in a list, but you wanted the list to have a distinguishing mark on each one to display whether the pest was a Fly or a Telemarketer. Then with the wrapper class, this would be easy:

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()) + 
  }
}

Then, in another place, if you had a list to represent the HouseFly by itself (not as an IPest but as a HouseFly) then you could give a different value for toString().

This isn't limited to toString(), but any other method that those classes may have that you might want to override to provide different functionality when the object is being represented as a IPest vs when it is being represented as a HouseFly or Telemarketer.

I hope my question makes sense.

My theory is that: if you are programming an API or anything that anyone will use, you should avoid concrete classes and try to use interfaces. But if you are writing client code directly and have zero expectation (or possibility) of code reuse then "programming to interface" seems like a not-so-big-deal.

I'm looking forward to any feedback. Am I way off-base here? Am I terrible at writing code? Hopefully Peter Meyer will give his input...

Community
  • 1
  • 1
ryvantage
  • 13,064
  • 15
  • 63
  • 112
  • Before long you add 14 more kinds of pests, and your IPest *depends on all of them*. Moreover, every user of IPest also depends on all of them. If you are your own worst enemy, you are welcome to try it out. – n. m. could be an AI May 09 '13 at 17:26

1 Answers1

3

To me, this is making the code a lot uglier for no obvious benefit. (That reads kind of harshly, which is not how I intend it...+1 for the actual question).

The big downside is your IPest class. As you continue to add possible pests, this class grows enormous and bloated with unused variables and code. If you have 30 different kinds of pests, then your IPest class has grown 15 times as large than your example with 2 kinds, with lots of extra code to support all of these classes.

Worse, the great majority of this code isn't actually relevant to the instantiated object. If the IPest is supposed to represent a HouseFly, then there are several instance variables (one for each other type of IPest) that are all empty, and a ton of unused code. Worse, what happens if IPest has more than one of its values not null? What is it? (BrundleFly the Telemarketer!)

Compare this with the pure interface, which grows no larger as more classes implement it (because it doesn't care.)

Finally, I don't think it's generally useful (or a good idea) to have a single conceptual idea (such as a single fly) represented by two (or more) objects such a HouseFly object and a IPest object...and more objects as you want to add more functionality. For each of your wrappers, you add another object, which is another object to potentially keep track of and update.

This isn't to say there couldn't be some very specialized case where something like this wouldn't be a good idea...but I'm not seeing it here, for the reasons I describe above.

Beska
  • 12,445
  • 14
  • 77
  • 112
  • Mmhmm... No problem on the harshness. My example comes from a very recent problem I faced, which is why I asked it. The problem I faced was: my program deals with "referrals," and these referrals can either come from people (one object) or companies (another object). We have a few screens where these objects are placed on lists and tables side-by-side as if they were one object. However, we wanted to be able to distinguish which were which in the toString method. I cannot think how to solve this problem without the wrapper class. – ryvantage May 09 '13 at 18:15
  • Perhaps I should adjust my thinking and use this wrapper-class approach only when absolutely necessary. You're right, as the list of possible objects to wrap grows, so does the complexity of the class. I still think my theory is correct, though, that in non-API code this might be a moot conversation.. – ryvantage May 09 '13 at 18:20