Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there good substitution for interface in has-a relationship in c++

Tags:

c++

oop

I am facing an OOP problem in GUI design, but let me illustrate it with animals example. Lets have following setup:

  • there is a base class Animal
  • any derived class can has-a Teeth
  • every animal with Teeth can Bite() <=> animals without Teeth cannot Bite()
  • every animal Bite() the same way (there is default implementation in Teeth class)

It is only natural for animal has-a Teeth, but now I need something like interface for has-a relationship. For example, if I have a vector of animals, how can I make every Bite() if they can?

std::vector<Animal *> animals;
animals.push_back(new dog());
animals.push_back(new fly());
animals.push_back(new cat());

void Unleash_the_hounds(std::vector<Animal *> animals) 
{
    //bite if you can!
}

I came up with several solutions but none seems to be perfectly fitting:

1.) every class with Teeth also implements interface IBiting. This solution, however, introduces a lot of code duplication, I would need to "implement" Bite() in every class:

class Cat : public Animal, public IBiting {
    Teeth teeth;
public:
    virtual void Bite() { teeth.Bite(); }
}

2.) Give every animal Teeth, but only allow some to use them. Note: syntax can be wrong - it is only illustration

class Animal{
    static cosnt bool canBite = false;
    Teeth teeth;
public:
    void Bite() { this->canBite ? teeth.Bite() : return; }
}

class Cat {
    static cosnt bool canBite = true;
}

3.) More inheritance - create class BitingAnimal and derive it. Well, this could work, but what if I needed derive (non)flying animals, some of them have teeth.

class Animal{}
class BitingAnimal : public Animal {
    Teeth teeth;
}

and use as BitingAnimal.teeth.Bite()

4.) multiple inheritance. This often is discouraged, and impossible in most languages, plus it is not logical for Cat to be Teeth.

class Cat : public Animal, public Teeth {
}

5.) Enum of classes that can bite - weird only by sound of it.


Or am I only over-complicating it and missed something important?

like image 592
wondra Avatar asked Aug 16 '14 19:08

wondra


1 Answers

An alternative that you didn't mention is to just provide an abstraction for the teeth, but implement the biting in the base class. This reduces the duplication because the derived classes only need to specify how to access the teeth instead of how to bite. By returning a pointer to the teeth, we can allow a null pointer to indicate that the animal has no teeth. Here is an example:

#include <vector>

struct Teeth {
  void bite() { }
};

struct Animal {
  virtual Teeth *teethPtr() = 0;

  void biteIfYouCan() { if (teethPtr()) teethPtr()->bite(); }
};

struct Dog : Animal {
  Teeth teeth;
  Teeth *teethPtr() override { return &teeth; }
};

struct Fish : Animal {
  Teeth *teethPtr() override { return nullptr; }
};

int main()
{
  Dog dog;
  Fish fish;

  std::vector<Animal *> animals {&dog,&fish};

  for (auto animal_ptr : animals) {
    animal_ptr->biteIfYouCan();
  }
}
like image 195
Vaughn Cato Avatar answered Oct 05 '22 23:10

Vaughn Cato