tags:

views:

250

answers:

5

I am trying to design an object model (for C#), and can't work out the best way to store the data. I'll try to use a simple example to illustrate this!

I have an object "pet", which could be one of, "cat", "dog" etc. So I have created an "pet" class with a "petType" enum to store this.

Now this is where it gets tricky. If an "pet" is a "cat", then its "food" could be one of "fish", "milk" etc. If it is a "dog" then its "food" could be "meat", "biscuits" or something.

Now should I create a big enum for "fish", "milk", "meat" and "biscuits" and somehow code it so that a "cat" cannot be assigned "food.meat"? It wouldnt really make sense for my "pet" class to have a "catfood" and "dogfood" enum, because thats not extensible and it will end up storing loads of enums that are null.

Is there an elegant solution to this that I'm not seeing?

A: 

First, cat and dog should probably be subclassed from pet, assuming there are some common properties of all pets.

Next, I'm not clear what you are planning to do with food. As an object model does a pet hold a type of food or will there be methods such as eat that will take food as an argument?

Tautologistics
A: 

i don't know about your cat, but my cats eat meat and very little else!

and eating table scraps like biscuits is bad for dogs (and cats)

but that aside, why are you screwing around with enums? and why does your 'pet' object have a 'petType' property? A simple/direct representation would be a Pet class with Cat and Dog subclasses, but a more practical representation would be a Pet interface that could be applied to any Animal object.

Steven A. Lowe
+2  A: 

Try #2. Seems to be correct

interface IPet { }

class Cat : IPet
{
    public void eat(CommonFood food) { }
    public void eat(CatFood food) { }
}

class Dog : IPet
{
    public void eat(CommonFood food) { }
    public void eat(DogFood food) { }
}

interface IFood { }

abstract class CommonFood : IFood { }

abstract class CatFood : IFood { }

abstract class DogFood : IFood { }

class Milk : CommonFood { }

class Fish : CatFood { }

class Meat : DogFood { }

class Program
{
    static void Main(string[] args)
    {
     Dog myDog = new Dog();
     myDog.eat(new Milk()); // ok, milk is common
     myDog.eat(new Fish()); // error
    }
}
abatishchev
A: 

I agree with Tautologistics on having Cat and Dog subclass Pet (or implement a Pet interface!), rather than having an explicit PetType enumeration and field. Explicit "type flags" are out of step with true OO design.

As for the food relationship, you might consider implementing two concepts:

  1. A "business rule" on Pet, implemented via an acceptsFood(FoodEnum food) method. This method would be responsible for checking the legality of a food assignment.
  2. A "favorite food" property of any Pet (subclass) instance that allows the individual pet to have a favorite food identified (out of the set of foods that are actually accepted by its species).
joel.neely
A: 

It sounds like you need to consider cat/dog food as sort of strategies, and compose appropriate food with appropriate pet using a simple factory.

Here is one variation in Ruby.

class CatFood
  attr_reader :items

  def initialize
    @items = ['fish', 'milk']
  end
end

class DogFood
  attr_reader :items

  def initialize
    @items = ['meat', 'biscuits']
  end
end

class NoFood
  attr_reader :items

  def initialize
    @items = []
  end
end

class Pet
  attr_reader :food

  def initialize(food)
    @food = food
  end
end

class PetFactory
  def create_dog
    Pet.new(DogFood.new)
  end

  def create_cat
    Pet.new(CatFood.new)
  end

  def create_unknown_pet
    Pet.new(NoFood.new)
  end
end
ragu.pattabi