Question: Today we looked at how to define and export objects in NodeJS. We covered basic syntax for both ES5- and ES6+ code. While we covered

Today we looked at how to define and export objects in NodeJS. We covered

basic syntax for both ES5- and ES6+ code. While we covered ES5 (and I have

provided a version of this assignment in ES5), you will most likely want to

use ES6-style syntax. It is the modern standard and is best practice for

writing any future projects. If you missed the lecture for whatever reason,

go back and get the notes from a fellow student or watch the lecture video

(if there is one available).

This lab activity will introduce to a common Javascript design pattern, while

also giving you practice in writing an Object Definition, and creating an NPM

module (both main concepts of this week).

*If you prefer to do this assignment in ES6 syntax, you can checkout the

main branch via **git**. From your terminal run*

git checkout main

Otherwise, go ahead and take a look at the **trackerService.js** file inside

the **tracker** directory. There have been some changes that should be

reviewed.

Primarily, the trackerService was turned into an Object Definition (in this

case using prototype notation) and much of the logic that was in the driver/

index.js in the previous lab was moved into the Object Definition. This object

represents a "tracker" or record of some activity (walking or running).

You'll also notice that the functions for calculating the calories burned while

walking or running have been turned into function objects. This makes it easy

to make references to them inside of the tracker definition. You'll notice

that the constructor sets the "exercise" property reference to one of them,

depending on what string is passed in. We'll call these the **exercise objects**

This exposes a specific concept. Consider the trackerService object. The only

thing that differentiates a walking trackerService and a running

trackerService is that the number of calories burned by each is calculated

with a different formula.

This means that the trackerService object has some behavior (calculating

calories burned) that is dependent upon data that it will not receive until

it is created at execution time. This idea is called **Dependency Injection**.

When the object is created we "inject" it with some data (in this case, a

string value representing the type of exercise) and it determines what

function object it will need for calculations. This concept will pop up again

and again in javascript so we need to practice it now.

The improvements made to the trackerService are good. But the problem is we

are not strictly following good design here. For every new exercise (formula)

that we want to add, we first have to create a new exercise function object,

then we need a new if statement to determine if a matching string has been

entered, and finally create the appropriate instance and assign it. None of

this has anything to do with recording the activity data.

It would be better for us to *encapsulate* all of this exercise stuff into its

own Exercise definition. That way we can simplify the code in trackerService

to just make a constructor call to Exercise. Plus, if we ever wanted to, we

could easily re-use the Exercise module. So that's what you are going to do!

DIRECTIONS:

1) First, create a new file inside the tracker folder named **Exercise.js**

*We are going to make a module that contains two files: trackerService.js and

Exercise.js. This will help you understand how npm works*

2) Open the new file in your VS Code Editor and then:

*a) Copy the definitions for the exercise objects from trackerService.js

(lines 60-70, inclusive) into Exercise.js*

*b) create an Exercise object definition with a constructor that accepts a

single parameter (see simpleObjects.js for an example). This parameter

will be a String.*

**Note** this is separate from the exercise objects placed in this file

*c) The Exercise module will have 2 instance properties. One will be named

**activity** and will reference one of the exercise function objects.*

**For Example:**

this.activity = new walking();

*The other will be named **type** and will hold the name of the activity.

Inside the constructor, check the string parameter's value. If it is

"walking" create an instance of walking exercise object and assign it to

the activity instance property, if it is "running" create and assign an

instance of the running exercise object, else throw an error. Finally,

create an instance property named **type** (if you haven't already done so

) and set its value to be the value of the parameter.*

*Up to this point, you are basically just moving the logic from

tracker constructor to the Exercise constructor*

*d) Add a function named **calculate** to the Exercise prototype.

The function accepts "weight" and "distance" parameters. It will pass

these values to the object's activity's calculate function and returns

the value.*

This would be called by writing something like

this.activity.calculate(weight, distance);

e) export the Exercise (VERY IMPORTANT)

module.exports = Exercise;

f) save the file

**3) Inside the trackerService file:**

*a) At the top, import the Exercise definition:*

var Exercise = require("./Exercise.js");

*b) In the tracker constructor, get rid of the redundant if/else branches

(We just moved these into Exercise). Replace them with a call to create a

new instance of Exercise(type).*

this.exercise = new Exercise(exercise);

This should automatically assign the exercise and calculate method.

You should leave the try catch statement alone.

*c) you can remove the local definitions for walking and running exercise

objects if you haven't already*

*d) note that we are exporting the tracker from here. if you read the

package.json file you'll notice that trackerService is the main file for

this module. Therefore whatever is exported from trackerService will be

made available whereever this is imported (not the Exercise).*

***e) save the file!***

4) Look at the package.json file. this is the module metadata from its initial

state. Currently it should some version 0.1.2 Go ahead and change the

version number to **0.1.3.**

*Then, run*

npm pack

This should create a new .tgz file (**tracker-0.1.3.tgz**).

5) Go into the ActivityTracker folder and run:

npm install ../tracker/tracker-0.1.3.tgz

check to make sure it all works by running (from within the ActivityTracker

folder):

node activityTracker.js

As long as it works then you're ready for the final step.

If it doesn't work, you may have to review these steps to make sure you've done

everything here. Fix whatever is wrong, then re-pack the tracker module,

and then re-install it into the ActivityTracker folder.

**Notice**

that the activityTracker.js file has more object literals to process AND the

last object is a swimming object. This version of the tracker cannot recognize

swimming activities. We'll fix this in the last step of the lab.

By modularizing the Exercises, we've made it much easier to reuse them (DRY),

made it easy to add more (extension) of them, separated the

functionality of the tracker from the functionality of the calculation (SRP),

and we abstracted/encapsulated the things that can change. We can add as many

"Exercises" to the program as we'd like, without ever having to change the

trackerService file. And Exercises can be "plugged in" elsewhere, if we need

them.

6) Now adding more types of activities is relatively easy. Currently the driver

does not recognize "swimming". All we need is an else if branch in the

Exercise constructor, and a new exercise function object for the

appropriate calculation to fix this.

Add a function Object named "swimming" to the Exercise.js file. Use the

following formula:

calories burned = (6 * Weight in KG * 3.5) / 200;

However, there is a slight problem. This formula only provides CALORIES BURNED

PER MINUTE not total calories, unlike the other two formulae.

To get the data we want, we need the time.

Since we also need all exercise function signatures to match, that means you

need all of the function objects to accept three parameters (weight, distance,

time).

In scenarios like this, it's ok to accept parameters that you will ignore.

So walking and running now need a time parameter added to the end of their

parameter lists, but thats it. There is no need to change their calculations.

And swimming should have matching signature. In the swimming calculation,

you can ignore distance parameter and return total calories using time and the

given formula.

We can add other types of exercise similarly, though we won't always

need to add a new parameter; the formula for swimming just happened to need

something more.

NOTE: the formula requires weight in kg but the array of Object literals in

activityTracker.js provides weight in pounds. 1 kg = 2.2 pounds, for the

purposes of this exercise.

Don't forget to update your package.json files

/*trackerService.js

Provides Activity Tracker Functionality utilizing Strategy Design Pattern.

Note that this file uses pre-ES6/prototyping syntax for defining a class.

While ES6 is a bit more firendly to people coming from Java and C++ and

other Class-based OO languages, this syntax is still valid and you *will*

see it in the wild. And it should not scare you when you do.

Activities: Walk/Run

Input: Exercise type

Distance (miles)

Weight (lbs)

Time (minutes)

Output: Calories Burned

Formula Sources:

https://lovandy.com/wellness/physical/calories-burned-by-walking.html AND

https://lovandy.com/wellness/physical/calories-burned-running.html

*/

//requires weight in lbs

//Time in hours

//distance in miles

//Constructor for the tracker Class.

var tracker = function(exercise, weight, distance, time) {

try{

if (exercise == "walking"){

this.exercise = new walking(); //exercise is a new instance of

//of the function objects below

} else if (exercise == "running") {

this.exercise = new running();

} else {

//throwing errors is as simply as throwing an object literal

//with a message property.

throw { message: "Unknown exercise!"}; //if the exercise is unknown

}

//Note that, like constructors in Java/C++, nothing is being returned

//from the constructor. But we have to use keyword this to indicate

//that we are createing an object here.

this.weight = Number(weight);

this.distance = Number(distance);

this.time = Number(time);

//catch any error thrown in object creation and re-throw it to

//calling module.

} catch (err){

console.log("Error recieved during object creation");

throw err;

}

};

//This calorie calculations for walking and running are

//implemented as Function Objects. These can be assigned

//to this.exercise at runtime.

var walking = function(){

this.calculate = function (weight, distance){

return 0.3 * weight * distance;

}

};

//requires weight in lbs, and distance in miles

var running = function(){

this.calculate = function (weight, distance){

return 0.63 * weight * distance;

}

};

//This is the Class Prototype. It is the "old" way of defining a JavaScript

//Class. It essentially provides functionality to all instances of tracker()

//Note how this is defined like a JSON object (Parameter Name: value). All JS

//objects at their core, are basically JSON (obviously)

tracker.prototype={

//runs the exercise specific calculation on internal variables

calculate: function() {

return this.exercise.calculate(this.weight, this.distance);

},

//speed is consistently calculated for all exercise times (distance/time)

calcSpeed: function(){

return this.distance/(this.time/60); //returns in miles per hour

}

};

//Export statements expose data we want to be "importable". In this case

//I want to export the tracker class (including its prototypes). I don't

//want to export the function objects from here because they are for

//internal use only. They are analogous to inner classes in this scenario.

module.exports = tracker;

//Your Lab assignment will be to further modularize this code and move most

//of the exercise specific stuff into an Exercise module.

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!