Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- =begin
- This is a simple example of some Object Oriented Programming focusing on a dynamic Animal class that includes some basic
- functionality to model animals being able to eat certain foods and gain stats from said food. This is only an abstraction
- to a base level of interaction and isn't wholly representative of the actual reality of feeding animals a diverse diet, but
- the overarching concepts of OO pop out in a few ways:
- 1. Each class has methods that maintain single responsibility. Essentially, they execute a single action or a related set of actions that make sense
- for the level of logical abstraction you are going for.
- 2. Each method on the class is doing one of a few things: ASKING the other class to do something with given data OR amending itself OR
- providing specific, structured data to work against without 'demanding' it from the other class by calling specific attributes
- on the class. Grouping is important!
- 3. Maintains its own state and does not have values set outside of the class itself. If there are changes made, the other class is ASKING THE BASE CLASS
- TO MAKE THE CHANGES, BUT NOT DIRECTLY FORCING IT TO DO SO IN THE SUPERCLASSES LOGIC AS THE CONSTRAINTS ARE SPECIFIC TO THE CLASS AND NOT TO THE IMPLEMENTATION
- AS A WHOLE, more or less.
- You don't want the 'Animal' class to forefully set any attributes on the 'Food' class ANYHWERE AT ALL. You want to pass data and ask the
- Food to change itself if anything, but literally hard-setting attributes from the Animal class is a bad idea.
- The main idea surrounding OO programming is that you want to have your classes interact with one another and send messages, but you should
- NOT have any class directly amending another class's attributes without ASKING it to do so through a method on the class to be amended, as this
- standardizes the ways in which the data can go in and out of your class.
- =end
- # We will start with the Animal class initially. The animal has some attributes and data associated with it - namely the
- # instance variables (@whatever) in the initialize method. This is how we persist data in runtime (when your code is running)
- # without having a database to reference against.
- # The data will not 'save' in the conventional sense, but persists in runtime space until the Ruby code is done executing.
- class Animal
- attr_reader :species, :name, :diet, :energy, :muscle, :fat
- # attr_reader is setting up 'getter' methods for all of the attributes present on initialization.
- # This allows you to call Animal.name, Animal.species, Animal.diet, etc to retrieve information about the
- # animal, but does NOT set up 'setter' methods that would allow you to set attributes by doing something like
- # Animal.name = "Carl". We do this because once the animal is created, we simply need to be able to retrieve information as
- # this is cleaner OO, and amendments should be passed in as a chunk of data to be processed if this were a specifically clean implementation of OO.
- def initialize(species, name, diet)
- # This thing throws an error if the Animal class is provided with a value for 'diet' that doesn't meet the level
- # of logical abstraction we have established to reach our goal of having an animal that has dietary contraints.
- if ![:omnivore, :herbivore, :carnivore].any?(diet)
- raise ArgumentError, "Please provide a valid diet type for your Animal: i.e. :omnivore, :herbivore, :carnivore"
- end
- # Setting up internal data representation and instance-based variable persistence...
- @species = species
- @name = name
- @diet = diet
- @hunger = 0
- @energy = 10
- @muscle = 10
- @fat = 10
- end
- # This is a simple utility method to grab the relevant athletic stats to be returned from methods that amend these attriutes directly
- # and need to be grouped as returned as such for things like control flow, validation, and whatever else we might need the info for
- # so we don't have to keep constructing it in other methods. (See #eat and #process_nutirition for examples of using it).
- def stats
- return {
- energy: @energy,
- muscle: @muscle,
- fat: @fat
- }
- end
- # How the animal eats. Notice that I am checking here wether or not the animal can eat the
- # food at all before even attempting to process the nutrition, as there's no reason to run code or make checks against anything if
- # the initial input is invalid to begin with.
- # I also made the animal lose energy out of 'annoyance' if they can't eat the food as this is how I wanted to model the reality that these animals exist in.
- def eat(food)
- if can_eat.include?(food.type)
- p "#{@name} ate the #{food.name}!"
- process_nutrition!(food.nutrition)
- return stats
- else
- p "#{@name} turns away from the #{food.name}!"
- @energy -= 1
- return stats
- end
- end
- # This is encapsulted here as it may be that we want to add nutrition from other items like vitamins, supplements, shots, IV fluid etc. at a later date.
- # This is quite literally how the animal's metabolic system is simulated to work, and the values I am dividing by could just as easily
- # we replaced by metabolite values instead of static intergers. I am keeping it encapsulated because it is, in fact, a different set of logic and
- # actions than simply having the ability to 'eat' a certain food, which could have its own levels of abstraction and ways of doing things
- # independent of how the animal processes the nutrition from the food itself.
- def process_nutrition!(nutrition)
- @energy += ((nutrition[:carbs] / 5) + (nutrition[:sugar] / 3) + (nutrition[:fat] / 12) + (nutrition[:protien] / 2)) / 2
- @hunger -= nutrition.map {|k,v| v }.inject(:+)
- @muscle += nutrition[:protien] / 5
- @fat += nutrition[:fat] / 3
- return stats
- end
- # This is simply set up as a reference to ensure that if an animal has a certain diet, they can eat certain foods.
- # I check against this with .include? (see #eat) as it's meant to represent a set of valid data for the Animal to work against.
- def can_eat
- if @diet == :carnivore
- return [:meat]
- elsif @diet == :herbivore
- return [:vegetable, :fruit, :starch, :seed]
- elsif @diet == :omnivore
- return [:vegetable, :fruit, :starch, :seed, :meat]
- end
- end
- end
- # The food class is fairly similar, but is designed to be consumed by another class or potentially amended by
- # things such as GMO engineering, age, or other environmental factors. This was a specific design decision, as
- # I figured it was more reasonable to be able to change the property of a food than an animal for what I was trying to
- # accomplish.
- class Food
- # Getter method setup.
- attr_reader :name
- # Getter & Setter method setup.
- attr_accessor :sugar, :fat, :protien, :carbs, :type
- # I am passing in a default hash here to ensure that, at the very least, food will have a 0
- # value for all of its attributes. This eliminates the potential for nil errors and will make it so that
- # non-muliplication math logic will maintain identity.
- def initialize(name, type, nutrition = {sugar: 0, fat: 0, protien: 0, carbs: 0})
- @sugar = nutrition[:sugar]
- @fat = nutrition[:fat]
- @protien = nutrition[:protien]
- @carbs = nutrition[:carbs]
- @name = name
- @type = type # i.e. :meat, :vegetable, :fruit, :starch, :seed
- end
- # Just a simple utility method like Animal.stats. This is atually what the animal is asking for
- # when it needs to process the nutrition gained from the food. We keep it here as there may be other modifiers not specific to the
- # animal that the food needs to amend on itself. Eventually, more transformations in the data may happen here,
- # so its a best to keep this encapsulated as we don't know what we are going to need to make that mechanic work later in development.
- def nutrition
- return {
- sugar: @sugar,
- fat: @fat,
- protien: @protien,
- carbs: @carbs
- }
- end
- def supplement(attr, value)
- end
- end
- # We are just making a bat and feeding it some fruit here. Run the code and see how it works!
- bat = Animal.new('fruit_bat', "Batty the Fruit Bat", :herbivore)
- apple = Food.new('apple', :fruit, {
- sugar: 80,
- fat: 10,
- protien: 5,
- carbs: 40
- })
- guava = Food.new('guava', :fruit, {
- sugar: 50,
- fat: 20,
- protien: 30,
- carbs: 10
- })
- p bat.eat(apple)
- p bat.eat(guava)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement