Interfaces without methods and fields

I have two classes: LivingCreature

and Animal

, which inherit from it.

I want to implement a class for every existing Animal, but since different animals have many functions, I want to classify Animals into several categories: Flying, Walking, Swimming, Carnivores, Vegetarian and so on ...

Each animal can be in several, even from the same area, such as Flying, Walking and Carnivore.

In addition, each category can contain several unique attributes, for example, a flying animal must consist of speed and type of food (for example, whether it sits on worms collecting trees, or "raiding" the land and collecting animals with their feet)

The first thing I definitely want to avoid is keeping a different set of fields for each specific implementation of the animal.

Now, since some categories are just binary (Carnivore or Vegan) and some are definitely not, I wonder what would be the correct way to implement it.

I lean towards interfaces for each category, even if they won't contain any methods, but I ran into a conflict:

  • It seems odd to have an interface for something as simple as isMeatEating that contains a single boolean field.
  • On the other hand, having multiple categories as animal fields and a few others implemented as interfaces sounds very wrong and confusing.

What would be the correct / best option here, design wise? (There may be a design pattern that matches this use case)

+3


source to share


1 answer


The pattern you seem to be describing is trait or perhaps a mixin .

Mixins is a language concept that allows a programmer to inject some code into a class. Mixin programming is a style of software development in which units of functionality are created in a class and then mixed with other classes.

Some languages, such as Scala, have built-in support for these concepts .

In Java 8, with default methods, we can do traits to some extent, and I say this because that's not the reason why default methods were designed.

Now it is difficult to come up with ideas for your design without complete information on what you are doing, but I will try to give an example of how I see it from my point of view.

Let me focus on food profiles: carnivores and herbivores. Carnivores eat other animals, herbivores eat plants or things of plant origin, and omnivores can eat both. Animals and plants are edible materials.

interface Edible {
   Collection<Nutrients> getNutrients();
}

interface Vegetal extends Edible {}
interface Animal extends Edible{}

      

All animals eat, so we can define animals as:

interface Animal<T extends Edible> extends Edible {

    Stomach getStomach();
    Collection<Bolus> masticate(T food);

    default void eat(T food) {
        Objects.requiresNonNull(food, "The food must not be null");
        for(Bolus bolus : this.masticate(food)) {
            this.getStomach().ingest(bolus);
        }
    }
}

      



Give me the details Stomach

and Bolus

, and let's just assume that all animals have a stomach and a way to chew food, turn it into a bolus and swallow it into the stomach.

Now we can finally reach our definition:

interface Carnivore extends Animal<Animal> { }
interface Herbivore extends Animal<Vegetal> { }
interface Omnivore extends Animal<Edible> { }

      

Now you can define

class Chicken implements Herbivore {}
class Lion implements Carnivore {}
class Bear implements Omnivore {}

      

Now he Chicken

can use only things of the type Vegetal

and Lion

only those things of the type Animal

, while he Bear

can eat any thing Edible

.

The more detail I use the default methods of my Animal interface, the less detail I will need to implement in those classes, and maybe this is a way to accomplish what you want.

I know I may not have been able to answer your question, but I hope I have at least given you some ideas on where you can continue your research and experiment with the perfect solution.

0


source







All Articles