5

I have one interface (let's say in C++) that has been implemented by some derived classes. Let's suppose that the interface is like this:

class IBase
{
  virtual bool method_1() = 0;
  virtual long method_2(const std::string str) = 0;
  // other methods
  virtual long method_10(int value) = 0;
};

and up to now 6 different derived classes (will be more in future) have implemented the interface. In lots of algorithms in my code, I have used this interface polymorphically. e.g:

// some usage
obj = getObject();
if (obj->method_1())
{
  return obj->method_2("Hello");
}
else
{
  return obj->method_10(12);
}

Over the time, I have found that there would be lots of methods that only one derived class implements them. So, the Q is that is it the right design to put these methods in the interface or should I put them directly in the derived classes? But note that:

  • If I put them in the interface, only one derived class will implement them and for the rest of the derived classes the implementation will be empty.
  • If I put them in the derived classes, my implemented algorithms will not be generic anymore and I need to check the type of the derived class during my algorithm implementation.

What is the right way to go ahead in this case?

  • 3
    It looks like there is a problem with your design. but with method names like " method_2" its difficult to give any constructive feedback. Can you give us the real situation? – Ewan Jul 29 at 9:18
  • @Ewan The real situation is somehow very domain-dependent and difficult be shared with the community. The main Q here is that how to go ahead with cases in which ONLY one derived class needs to implement a method from a common interface. – Gupta Jul 29 at 9:23
  • 1
    @Hoodi if only one class implements it, it's not a common interface. It sounds like you need to be delegating those parts of the algorithm to the appropriate implementation (which would negate the need for the type check). – Ant P Jul 29 at 9:46
  • You aren't using the interface polymorphically. Polymorphically would be calling obj->do_stuff() and each implementation contains its own logic for whether to call method_2, method_10, or something else you haven't thought of yet. – TKK Jul 29 at 17:35
  • You should analyze the functions where you currently use these interfaces. Chances are that you can probably group the methods in subgroups (so a method calling method_2 probably will call method_10 but is never going to call method_3), this should show you that either the methods are too detailed and you can hide them under an implementation using more general methods or that your IBase could be separated into multiple separate interfaces (keep in mind that some class might implement both in a particular case, but others will just implement the specific one they need) – Bakuriu Jul 29 at 18:43
12

How to go ahead with cases in which ONLY one derived class needs to implement a method from a common interface?

If you end up in this situation then generally you have a problem with your design and should refactor.

For example in your calling code you call multiple methods on the class and make decisions based on the results of these methods. This is probably wrong. Instead you should have the logic encapsulated in the class and interface:

interface IShoppingBasket 
{
    void AddItem(Item item); //only one method
}

class ElectronicsShoppingBasket : IShoppinBasket
{
    public void AddItem(Item item)
    {
        if(item.Type == software) //logic is in the class not the calling code
        {
            this.softwareLicences.Add(item); //private method only in this implementation
        }
        else
        {
            this.physicalGoods.Add(item); //private method only in this implementation
        }
    }
}

I'm not exposing IsSoftwareLicence() or AddPhysicalGoods() because they are a concern only of the specific implementation. Not the generic interface.

This allows me to make multiple derived classes with differing functionality, but have them all perform the same task.

For the case where specific ShoppingBasket implementations need extra information, this can often be viewed as a dependency of the shopping basket:

class ElectronicsShoppingBasket : IShoppinBasket
{
    public void ElectronicsShoppingBasket (ICustomerDetailsProvider custDet)
    {
        this.custDet = custDet
    }

    public void AddItem(Item item)
    {
       if(this.custDet.GetAddress().Country == Country.NorthKorea)
       {
            throw new Exception("embargoed country"); 
       }
       .....
    }
}

Here the constructor is not inherited or part of the interface, but we enforce the requirement that a CustomerDetailsProvider is in place where ever we have an ElectronicsShoppingBasket

Again, the calling code doesn't have to know about the customer info or have any logic

class CallingCode
{
     public CallingCode(IShoppingBasket sb) //pass me anything as long as it has AddItem
     {
         this.sb = sb;
     }

     public void AddItem(Item item)
     {
          this.sb.AddItem(item); //if you need any other details that's your problem
     }
}
  • OK, good example. Now suppose that your Items for Eelctronic and NonElectronic baskets differ and they cannot share any commonalities. What do you do? – Gupta Jul 29 at 9:59
  • the example is using ShoppingBasket as the common class. not Item. If Item needs multiple implementations I would do the same as I have done with shopping basket – Ewan Jul 29 at 10:06
  • Or even suppose that ALL of the ShoppingBasket implementations need customer info to be set/get, except one of them (For a InPlaceShoppingBasket case for buying from the supermarket directly). What about this case? – Gupta Jul 29 at 10:13
  • 2
    I've added some code on an example of how to cover that case. I think you are misunderstanding interfaces. It's the calling code that is telling the class what it needs to have, not the class telling he calling code what it needs to call – Ewan Jul 29 at 10:29
  • @Hoodi If the general workings of a Shopping Basket need customer info, then the interface should define that contract. If one of the implementations doesn't care about customer info... then they should still follow the interface (a method exists and doesn't throw). Interfaces declare contracts. Interfaces don't care about how things work internally, and should be generic. AddItem() is a great example - the interface doesn't care whether we're just adding to an array, or dispatching a message to take something off a shelf, or just letting a db know that there's one less soda in stock. – Delioth Jul 29 at 18:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.