Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Episode1 Homework #2

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

johnnygoodman
Copy link

Added Human class that likes bacon and tacos but does not like bamboo.

@johnnygoodman
Copy link
Author

I do not have a handle on the extra credit #1 question. I created a Food class that takes a name and optionally turns it into a string. Then I went through the code and changed places where there were symbols into a class to a new instance of Food.

I think I would want to do this in the case there were other things about food I wanted to track - freshness, packaging, inventory, etc.

As it stands, I've replaced a lot of symbols with longer calls to no immediate benefit. This is fine if it was the goal, but I want to make sure I really get it - I feel more like I copy/pasted...

Thanks!

@jwo
Copy link
Member

jwo commented Apr 29, 2012

There's a bunch of ways this could have gone --- here's one way that might have had a bigger impact with a Food class. Rather than having a string or symbol pointer about what an animal likes to eat, you can just store the food object itself. Something like:

def acceptable_foods
  [Bacon.new, Tacos.new]
end

or

def acceptable_foods
  [Food.new(:bacon), Food.new(:tacos)]
end

Then, the food barge would be something like (in rspec):

animal = Human.new
barge.food_for(animal).should eq([Food.new(:bacon), Food.new(:tacos)])

I think the Tacos.new looks best, but would require the most class definitions.

@ralphos
Copy link

ralphos commented Apr 29, 2012

Hi, I thought I'd add a bit of discussion :)

I can't figure out how you'd apply Food.new(:bacon), to the other rspec tests?

For example:

it "should like bacon" do
  Human.new.likes?(Food.new(:bacon)).should be_true
end

The way I see it, you somehow need to be able to compare that object instance (of Food.new) to your array in the acceptable_food method, but when calling include? you can't get access to that same instance, if that makes sense?

I'm guessing you probably couldn't use the .should be_true in the test above, and instead reframe it to maybe use .should be eq(...)?

What do you guys think?

@jwo
Copy link
Member

jwo commented Apr 29, 2012

It's a good question... In most programming languages you can ask 5 == 5 and be assured it's true.

But when it comes to object, determining their equality is left up to the programmer. Ruby is similar. If you have two objects, then they must be EXACTLY equal in every way -- that is, they must be the same pointer.

class Phone
end

puts Phone.new == Phone.new
 => false 

So you need to implement an "object-equality" method. In ruby, that's the "==" method...

class Phone
  attr_reader :model
  def initialize(model)
    @model = model
  end
  def ==(other)
    other.model == model
  end
end

puts Phone.new("iPhone") == Phone.new("iPhone")
 => true

@ralphos
Copy link

ralphos commented Apr 29, 2012

ahhh.. so what you're really comparing in your example is whether the Phone object's model attribute is the same or equal to the other Phone's model attribute. In the case above I assume they are just comparing whether the model attributes are equal strings, since @model = "iPhone"?

I did a little refactoring for my answer and will push the latest commit. Was simply a case of making the tests pass by comparing the name attribute of each food as you have done above.

@johnnygoodman
Copy link
Author

You said that you'd prefer the Taco.new approach.

For that to work, wouldn't we need a reliable way to compare classes or objects of classes?

Your earlier example showed that this is false because there are two unique pointers:

puts Phone.new == Phone.new
 => false 

So, how do we know what's a Taco? The only way forward I see is to say "if this object.class == Taco, then I'm going to say they are equal".

I made an example...

Class Defs

RSpec Tests

If Taco.new is the elegant way, and I need to test for Taco-ness, and I've got a Taco class instead of Taco.name or Phone.model, is there another way I should be doing it or am I on the right path?

Thanks!

@jwo
Copy link
Member

jwo commented Apr 30, 2012

@johnnygoodman I think you're right that if all Tacos are equal, then the object-equality would look something like:

class Taco

  def ==(other)
    other.is_a? Taco
  end
end

…t rvm use 1.9.2 would break gems, as it has in other directories I work in. The cost of ignorance...
@johnnygoodman
Copy link
Author

Regarding the FoodBarge extra credit #2 problem from Episode 1 homework...

I am learning how to use classes in Ruby and now will get a chance to learn some of the "why" behind class design with these questions. This is exactly why I signed up. Thank you.

Questions:

1. Class Org Philosophy

In the Animal module, feed and eat are very similar. I did it this way because the instructions call for me to feed the panda via "panda.feed(food)".

I think if I was going free form I would do something more like:

zookeeper = Zookeeper.new
zookeeper.feed(panda)

and resolve it in the Zookeeper#feed method

or something like:

food = @foodbarge.food_for(panda)
zookeeper = Zookeeper.new
zookeeper.feed(panda, food)

I'm not sure which one would be the best practice, or if there's a reason why panda.feed(food) makes the most sense.

2. Lions can't just eat one (but Pandas can).

The way I have it setup, a lion always has to eat a Wildebeest and a Zeebra. We can't feed him 4 Wilde's and 2 Zeebra's. Maybe this is just outside of scope for the exercise, or maybe I've done it wrong...

3. Defining setter/getter at the module level.

This worked:

Module Animal
  attr_reader :meals

Is it a good idea Ruby wise? The alternative was to make each Lion, Panda, etc class have this definition.

4. Equality Override Method ain't DRY

I suspect this could be refactored:

class Taco
  def ==(other)
    other.is_a? Taco
  end
end

class Bacon
  def ==(other)
    other.is_a? Bacon
  end
end

class Bamboo
  def ==(other)
    other.is_a? Bamboo
  end
end

Initially, I was going to:

  1. Create a Food module.
  2. Paste the method "up a level" from Bamboo to Food module.
  3. Include Food in each Taco, Bacon, Bamboo type class.

This didn't pan out because Bamboo is hard coded in the Bamboo class. I considered replacing "Bamboo" with other.class but then I'm defining other, which has a class and then asking if it matches itself. At that point, I can shorten the method by just saying 1=1...how do I "get at it"?

5. @foodbarge vs foodbarge

Using defined?(@Meals) told me it is an instance variable. I believe this means that for each object of the class (each instance), @Meals will hold the number of means for that object.

I see a lot of:

panda = Panda.new
taco = Taco.new

I'm wondering why the example has:

@foodbarge = FoodBarge.new

Shouldn't it be:

foodbarge = FoodBarge.new

@jwo
Copy link
Member

jwo commented May 2, 2012

  1. In your example, the zookeeper knows an awful lot about the other two classes. And that's probably ok, but in a larger system, you might want to have the equivalent of a 'transaction' class. So the Zookeeper creates a FeedsAnimal class that knows it needs to get food from the barge to give to the animal.

It's all about reducing the number of classes that your classes know about.

  1. I think your idea is fine; I had thought that I could eat either bacon or tacos, not always bacon + tacos... Semantics, to be sure.
  2. Having the attr_reader on the Module is perfectly fine, ruby wise.
  3. Yes, that certainly looks like it could be cleaned up. Something like this:
class Taco
  include FoodAttributes
end

module FoodAttributes
  def ==(other)
    other.is_a self.class
  end
end
  1. Yes, that's right, @ indicates it is an instance variable.... It's more thinking if you had a big Zoo class later on, you'd probably have a @foodbarge that was the 1 barge for the zoo.

Hope that helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants