Reputation:
I'm creating a BMR calculator, and I have a class called User. This class contains all the methods used to calculate the BMR, as well as a constructor to pack the user's data (age, gender, weight, height) together.
The code is:
public class User {
int age;
String gender; // todo: use an Enum
double height; // height stored in cm, weight in kg (so if user enters in feet/lbs, conversions are done to cm/kg and *THEN* passed through to constructor below)
double weight;
double activityMultiplier; // todo: use an Enum (possibly)
int bmr;
public User(int age, String gender, double height, double weight,
double activityMultiplier) {
this.age = age;
this.gender = gender;
this.height = height;
this.weight = weight;
this.activityMultiplier = activityMultiplier;
bmr = calcBMR();
}
/**
* If user input is correct, this method will calculate the BMR value of the user given their input and measurement choices.
*
* @param None
* @return BMR Value
*/
public int calcBMR() {
int offset = gender.equals("M") ? 5 : -161;
// This is the body of the calculations - different offset used depending on gender. Conversions to kg and cm done earlier so no conversions needed here.
// The formula for male and female is similar - only the offset is different.
return (int) (Math.round((10 * weight) + (6.25 * height) - (5 * age) + offset)); // This is the Miffin St-Jeor formula, calculations done in cm/kg
}
/**
* If the user selects the TDEE option, this method will be executed after the calcBMR() method.
* A value from the calcBMR() method will be passed down to this method, and is multiplied
* by the activity level parameter passed into this method.
*
* @param bmr (output from calcBMR() method
* @return TDEE Value
*/
public int calcTDEE(int bmr) {
return (int) Math.round(calcBMR() * activityMultiplier);
}
}
My concern is that I'm not sure that the way I'm initialising the value for bmr in the constructor (bmr = calcBMR()
) is correct. I can't calculate the bmr till the users age, gender, height and weight are recorded and stored in variables (which are what the 5 lines above do). Is this programming structure okay? I.e. when a User object is created, the age, gender, height and weight are stored in variables, and THEN a method is called within the constructor to calculate and store another value.
Is there a better way to do this? If not, do I need to do this.bmr = calcBMR()
or is bmr = calcBMR()
fine?
Note the User object is created in a seperate class. The reason I'm mainly confused is because I'm not passing a bmr parameter into the constructor, I'm using a method return value to initiate the value of an instance variable instead.
Upvotes: 2
Views: 747
Reputation: 22972
Better to separate the calculation from the construction.
I personally believe we should avoid doing this. According to me for any person using Class
which is constructing your Object
will be unaware of the fact that bmr
is also being calculated in the User
by seeing this particular constructor, until he look into the code. When you are building API
you should make it more readable for the consumer who will going to use your User
, apart from that it's totally valid. Moreover, as suggested by Jeff Storey
if you don't want user Object
s to use your calcBMR
you should make it private
.
One more point I want to add here that bmr
is one of the Health
related parameter of the Person
so you should collect all this kind of parameters in separate class which can include bodyFat
, bmr
, medicalAge
etc in it and pass your User
Object
in constructor of this class
. So, ultimately Health
parameters can only be constructed with valid user only.
Upvotes: 0
Reputation: 1599
I think your way is ok. However, it is better if you use all your properties is "private" and have get/set for them. So, when another class extends your class, we don't have to worry about messy things. With the calcTDEE method, I think you just need to put your bmr property inside it because it is set in your constructor. So, at the time you call this method, the bmr has the right value.
public int calcTDEE() {
return (int) Math.round(bmr * activityMultiplier);
}
Hope this help.
Upvotes: 0
Reputation: 57192
It's ok syntactically, however you should not call an override-able (public/protected non-final) method from your constructor. If someone overrides it, it could mess up the construction of your object. Calling helper methods from a constructor is fine, just make it private or final.
this.bmr = calcBMR()
is the same as
bmr = calcBMR()
Saying bmr.calcBMR()
wouldn't make sense because the calcBMR
method is on the User
object. bmr
is an int
so it doesn't have a method named calcBMR
Whether you use this
or not is a matter of your preference. It only really makes a difference if you have a local variable also named bmr
, and then you are explicitly calling the instance variable rather than the local. In general, it's confusing to have a local and instance variable with the same name.
Your calcTDEE
method is a little bit off though. You can just use the value of bmr
, not pass it in or recalculate it, so it would be
public int calcTDEE() {
return (int) Math.round(bmr * activityMultiplier);
}
Upvotes: 3