Object-oriented program for a calorie counter sporting polymorphic methods for both genders.
Principles to adhere to:
- Uniform Access Principle
- Polymorphism
- Dependency inversion principle through the Factory Method pattern
- Single responsibility principle
Concerns for future extension:
- BPM curve for exercise acts of longer duration, can this be transparently added?
Other concerns:
- Unit prefixes for names, e.g.
weight_kg
,exercise_duration_min
. Are these bad? - Overengineered? Yes, I'm not going to argue against that; we don't have more than one method in our classes which eliminates the need to have them at all. Assuming we had more than one method, however, is it still overdone?
- Are any of the principles I claimed to follow not followed or properly followed? Especially thinking of the Factory Method pattern.
By the way, the formula for calculating calories burned doesn't seem to be right, but that doesn't really matter for our purposes.
# oo-calorie_counter.py
from abc import ABC, abstractmethod
def main():
"""Setup and initialization.
"""
person_attributes = get_person_attributes()
person = PersonFactory.make_person(person_attributes)
print("You've burnt approximately {0} calories".format(round(person.calories_burned, 1)))
def get_person_attributes():
"""Prompts a user for their details
"""
person_attributes = {
'gender': input('Your gender: '),
'weight_kg': int(input('Your weight in Kg: ')),
'age': int(input('Your age: ')),
'bpm': int(input('Your heart rate: ')),
'exercise_duration_min': int(input('Your your exercise duration in minutes: '))
}
return person_attributes
class PersonFactory:
@staticmethod
def make_person(person_attributes):
"""
Instantiate the proper a person of proper gender based on the provided attributes.
Args:
person_attributes: A dictionary containing the keys:
('gender', 'age, 'weight_kg', exercise_duration_min, bpm)
"""
gender = person_attributes['gender']
del person_attributes['gender']
if gender.lower() == 'male':
return Male(**person_attributes)
elif gender.lower() == 'female':
return Female(**person_attributes)
else:
raise RuntimeError('Unknown gender')
class Person(ABC):
def __init__(self, weight_kg, bpm, age, exercise_duration_min):
self.weight_kg = weight_kg
self.age = age
self.bpm = bpm
self.exercise_duration_min = exercise_duration_min
@property
@abstractmethod
def calories_burned(self):
raise NotImplementedError
class Male(Person):
@property
def calories_burned(self):
return ((self.age * 0.2017) - (self.weight_kg * 0.09036) + (self.bpm * 0.6309) - 55.0969) * self.exercise_duration_min / 4.184
class Female(Person):
@property
def calories_burned(self):
return ((self.age * 0.074) - (self.weight_kg * 0.05741) + (self.bpm * 0.4472) - 20.4022) * self.exercise_duration_min / 4.184
if __name__ == '__main__':
main()