Created
June 6, 2024 02:37
-
-
Save thiagoadsix/85085763062990ab2fc3238343dcb20e to your computer and use it in GitHub Desktop.
This is will be a part of my code that I had to implement Strategy design pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
AssessmentMeasures, | |
AssessmentResult, | |
BodyMeasurement, | |
GenderType, | |
} from "@domain/shared/entities/types"; | |
export abstract class AssessmentAbstract { | |
abstract calculateResults( | |
input: AssessmentAbstract.Input | |
): AssessmentAbstract.Output; | |
protected calculateAge(birthDateString: string): number { | |
const birthDate = new Date(birthDateString); | |
const today = new Date(); | |
let age = today.getFullYear() - birthDate.getFullYear(); | |
const monthDiff = today.getMonth() - birthDate.getMonth(); | |
if ( | |
monthDiff < 0 || | |
(monthDiff === 0 && today.getDate() < birthDate.getDate()) | |
) { | |
age--; | |
} | |
return age; | |
} | |
protected calculateSumOfSkinfolds( | |
assessmentMeasures: AssessmentMeasures, | |
skinfoldSites: string[] | |
): number { | |
return skinfoldSites.reduce((sum, prop) => { | |
return sum + assessmentMeasures[prop]; | |
}, 0); | |
} | |
protected calculateBodyMetrics( | |
sumOfSkinfolds: number, | |
age: number, | |
coefficients: { a: number; b: number; c: number; d?: number } | |
): { bodyDensity: number; bodyFatPercentage: number } { | |
let bodyDensity; | |
if (coefficients.d !== undefined) { | |
bodyDensity = | |
coefficients.a - | |
coefficients.b * sumOfSkinfolds + | |
coefficients.c * Math.pow(sumOfSkinfolds, 2) - | |
coefficients.d * age; | |
} else { | |
bodyDensity = | |
coefficients.a - coefficients.b * sumOfSkinfolds + coefficients.c * age; | |
} | |
const bodyFatPercentage = (4.95 / bodyDensity - 4.5) * 100; | |
return { | |
bodyDensity: Number(bodyDensity.toFixed(2)), | |
bodyFatPercentage: Number(bodyFatPercentage.toFixed(2)), | |
}; | |
} | |
protected calculateBMR( | |
gender: GenderType, | |
weight: number, | |
height: number, | |
age: number | |
): number { | |
if (gender === "male") { | |
return 66 + 13.7 * weight + 5 * height - 6.8 * age; | |
} else { | |
return 655 + 9.6 * weight + 1.8 * height - 4.7 * age; | |
} | |
} | |
protected calculateFatMass( | |
weight: number, | |
bodyFatPercentage: number | |
): number { | |
return weight * (bodyFatPercentage / 100); | |
} | |
protected calculateLeanMass(weight: number, fatMass: number): number { | |
return weight - fatMass; | |
} | |
protected calculateIdealBodyFat(gender: GenderType): { | |
min: number; | |
max: number; | |
} { | |
if (gender === "male") { | |
return { min: 8, max: 20 }; | |
} else { | |
return { min: 15, max: 30 }; | |
} | |
} | |
protected calculateIdealWeight( | |
leanMass: number, | |
idealBodyFat: { min: number; max: number } | |
): { | |
min: number; | |
max: number; | |
} { | |
const minWeight = leanMass / (1 - idealBodyFat.min / 100); | |
const maxWeight = leanMass / (1 - idealBodyFat.max / 100); | |
return { min: minWeight, max: maxWeight }; | |
} | |
} | |
export namespace AssessmentAbstract { | |
export type Input = { | |
assessmentType: string; | |
assessmentMeasures?: AssessmentMeasures; | |
bodyMeasurement?: BodyMeasurement; | |
gender: GenderType; | |
birthDate: string; | |
}; | |
export type Output = AssessmentResult | null; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
AssessmentResult, | |
AssessmentType, | |
} from "@domain/shared/entities/types"; | |
import { PhotosImpl } from "./implementations/photos.impl"; | |
import { Pollock3Impl } from "./implementations/pollock-3.impl"; | |
import { Pollock7Impl } from "./implementations/pollock-7.impl"; | |
import { AssessmentAbstract } from "./assessment.abstract"; | |
export class AssessmentStrategy { | |
private strategies: Map<AssessmentType, AssessmentAbstract>; | |
constructor() { | |
this.strategies = new Map(); | |
this.strategies.set("photos", new PhotosImpl()); | |
this.strategies.set("pollock_3", new Pollock3Impl()); | |
this.strategies.set("pollock_7", new Pollock7Impl()); | |
} | |
run(input: AssessmentStrategy.Input): AssessmentStrategy.Output { | |
const strategy = this.strategies.get( | |
input.assessmentType as AssessmentType | |
); | |
if (!strategy) { | |
throw new Error( | |
`Assessment type (${input.assessmentType}) is not supported.` | |
); | |
} | |
if ( | |
input.assessmentType === "pollock_3" || | |
input.assessmentType === "pollock_7" | |
) { | |
if (!input.assessmentMeasures || input.assessmentMeasures == null) { | |
throw new Error( | |
`Assessment measures are required for ${input.assessmentType}.` | |
); | |
} | |
} | |
return strategy.calculateResults(input); | |
} | |
} | |
export namespace AssessmentStrategy { | |
export type Input = AssessmentAbstract.Input; | |
export type Output = AssessmentResult | null; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { AssessmentAbstract } from "../assessment.abstract"; | |
export class PhotosImpl extends AssessmentAbstract { | |
calculateResults(input: AssessmentAbstract.Input): AssessmentAbstract.Output { | |
const { assessmentMeasures } = input; | |
if (assessmentMeasures) { | |
const { birthDate, gender } = input; | |
const age = this.calculateAge(birthDate); | |
const bmr = this.calculateBMR( | |
gender, | |
assessmentMeasures.weight, | |
assessmentMeasures.height, | |
age | |
); | |
const idealBodyFat = this.calculateIdealBodyFat(gender); | |
return { | |
bmr, | |
idealBodyFatMax: idealBodyFat.max, | |
idealBodyFatMin: idealBodyFat.min, | |
}; | |
} | |
return null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
FEMALE_3_COEFFICIENTS, | |
MALE_3_COEFFICIENTS, | |
} from "@domain/shared/constants"; | |
import { AssessmentAbstract } from "../assessment.abstract"; | |
export class Pollock3Impl extends AssessmentAbstract { | |
calculateResults(input: AssessmentAbstract.Input): AssessmentAbstract.Output { | |
const { gender, birthDate, assessmentMeasures } = input; | |
const genderSpecificProps = | |
gender === "male" | |
? ["chest", "abdomen", "thigh"] | |
: ["triceps", "suprailiac", "thigh"]; | |
const coefficients = | |
gender === "male" ? MALE_3_COEFFICIENTS : FEMALE_3_COEFFICIENTS; | |
const sumOfSkinfolds = this.calculateSumOfSkinfolds( | |
assessmentMeasures, | |
genderSpecificProps | |
); | |
const age = this.calculateAge(birthDate); | |
const metrics = this.calculateBodyMetrics( | |
sumOfSkinfolds, | |
age, | |
coefficients | |
); | |
const bmr = this.calculateBMR( | |
gender, | |
assessmentMeasures.weight, | |
assessmentMeasures.height, | |
age | |
); | |
const fatMass = this.calculateFatMass( | |
assessmentMeasures.weight, | |
metrics.bodyFatPercentage | |
); | |
const leanMass = this.calculateLeanMass(assessmentMeasures.weight, fatMass); | |
const idealBodyFat = this.calculateIdealBodyFat(gender); | |
const idealWeight = this.calculateIdealWeight(leanMass, idealBodyFat); | |
return { | |
bodyDensity: metrics.bodyDensity, | |
bodyFatPercentage: metrics.bodyFatPercentage, | |
sumOfSkinfolds, | |
bmr, | |
fatMass, | |
leanMass, | |
idealBodyFatMin: idealBodyFat.min, | |
idealBodyFatMax: idealBodyFat.max, | |
idealWeightMin: idealWeight.min, | |
idealWeightMax: idealWeight.max, | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { AssessmentResult } from "@domain/shared/entities/types"; | |
import { | |
FEMALE_7_COEFFICIENTS, | |
MALE_7_COEFFICIENTS, | |
} from "@domain/shared/constants"; | |
import { AssessmentAbstract } from "../assessment.abstract"; | |
export class Pollock7Impl extends AssessmentAbstract { | |
calculateResults(input: AssessmentAbstract.Input): AssessmentResult { | |
const { gender, birthDate, assessmentMeasures } = input; | |
const genderSpecificProps = | |
gender === "male" | |
? [ | |
"chest", | |
"abdomen", | |
"thigh", | |
"subscapular", | |
"axilla", | |
"calf", | |
"triceps", | |
] | |
: [ | |
"triceps", | |
"suprailiac", | |
"thigh", | |
"subscapular", | |
"axilla", | |
"calf", | |
"abdomen", | |
]; | |
const coefficients = | |
gender === "male" ? MALE_7_COEFFICIENTS : FEMALE_7_COEFFICIENTS; | |
const sumOfSkinfolds = this.calculateSumOfSkinfolds( | |
assessmentMeasures, | |
genderSpecificProps | |
); | |
const age = this.calculateAge(birthDate); | |
const metrics = this.calculateBodyMetrics( | |
sumOfSkinfolds, | |
age, | |
coefficients | |
); | |
const bmr = this.calculateBMR( | |
gender, | |
assessmentMeasures.weight, | |
assessmentMeasures.height, | |
age | |
); | |
const fatMass = this.calculateFatMass( | |
assessmentMeasures.weight, | |
metrics.bodyFatPercentage | |
); | |
const leanMass = this.calculateLeanMass(assessmentMeasures.weight, fatMass); | |
const idealBodyFat = this.calculateIdealBodyFat(gender); | |
const idealWeight = this.calculateIdealWeight(leanMass, idealBodyFat); | |
return { | |
bodyDensity: metrics.bodyDensity, | |
bodyFatPercentage: metrics.bodyFatPercentage, | |
sumOfSkinfolds, | |
bmr, | |
fatMass, | |
leanMass, | |
idealBodyFatMin: idealBodyFat.min, | |
idealBodyFatMax: idealBodyFat.max, | |
idealWeightMin: idealWeight.min, | |
idealWeightMax: idealWeight.max, | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment