Home » Javascript » Verbose OO Javascript assignment

Verbose OO Javascript assignment

Posted by: admin November 1, 2017 Leave a comment

Questions:

I’ve been given an assignment that I have been working on, and the specs and documentation I am going to copy and paste below.

ChangeDispenserController Class
Provides a simple API for the vending machine change dispenser.

Methods

calculateChange(unitsTendered, price)
  1. unitsTendered: An array of instances of some kind of MonetaryUnit subclass. The units tendered all have the same currency and the amount tendered is greater than price.

  2. price: Number. The units tendered match the currency for price, which is why price is just a number.
    Returns true if change will successfully be dispensed, otherwise returns false to issue a refund and cancel the product.

ChangeDispenser Class
An instance of ChangeDispenser is programmed to physically know what change it has to dispense and physically how to dispense it. ChangeDispenser is a thin JavaScript layer over native code.

Properties

For every currency unit available, the JavaScript layer dynamically defines properties that match the class name of the monetary units using Object.defineProperty. It defines the properties as read-only properties using getter methods that return new arrays. The new arrays returned from these properties are not referenced internally. For example, someChangeDispenserInstance.UnitedStatesPennyUnit would give you an array containing all available instances of UnitedStatesPennyUnit.

Methods

  1. unitsAvailable(currency)
    Describes the types of monetary units available to dispense.
    currency: A String value for the currency, i.e. “USD”.
    Returns an array of objects describing the availablity of monetary units that match the curreny passed in, sorted in descending monetaryValue:

    [
        // ...
        {
            propertyName: String (i.e. "UnitedStatesDimeUnit"),
            unitInfo: {
                monetaryValue: 0.1,
                currency: "USD",
                currencySymbol: "$"
            }
        },
        // ...
    ]
    
  2. dispense(units)
    Physically dispenses change for all of the monetary unit instances that you pass in.
    units: An array of instances of some kind of MonetaryUnit subclass.
    Throws an error synchronously if any instance is not in the monetary unit properties, i.e. someChangeDispenserInstance.UnitedStatesPennyUnit.

And finally there is the starting bit of code provided by the assignment itself found below.

Style Notes
You should do your best to match the style in the code provided, including line spacing, tabulation (tabs not spaces), and tabulation on empty lines (should have indentation). Variable names should be clear and descriptive and contain no abbreviations. Open curly braces should not be preceded by a line break, and you should not use the “cuddled else”.

/*
This is John Resig's simple inheritance, updated for ES 5.1. Based on   http://ejohn.org/blog/simple-javascript-inheritance
*/

var Class = (function() {
var hasFunctionSerialization = /xyz/.test(function() {xyz;});
var referencesSuper = hasFunctionSerialization ? /\b_super\b/ : /.*/;

// The base Class implementation (does nothing)
function Class() {}

// Create a new Class that inherits from this class
Class.extend = function(properties) {
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    var prototype = Object.create(_super);

    // Copy the properties over onto the new prototype
    for (var name in properties) {
        // Check if we're overwriting an existing function
        if (typeof properties[name] == "function" && typeof _super[name] == "function" && referencesSuper.test(properties[name])) {
            prototype[name] = (function(name, originalFunction) {
                return function() {
                    var temporary = this._super;
                    this._super = _super[name];
                    var returnValue = originalFunction.apply(this, arguments);
                    this._super = temporary;
                    return returnValue;
                };
            })(name, properties[name]);
        }
        else {
            prototype[name] = properties[name];
        }
    }

    // The new constructor
    // All construction is actually done in the init method
    var SubClass = typeof prototype.init === "function" ? prototype.init : function() {};

    // Populate our constructed prototype object
    SubClass.prototype = prototype;

    // Enforce the constructor to be what we expect
    prototype.constructor = SubClass;

    // And make this class extendable
    SubClass.extend = Class.extend;

    return SubClass;
};

return Class;
})();

var MonetaryUnit = Class.extend({
init: function(options) {
    var monetaryValue = parseFloat(options.monetaryValue);
    if (isNaN(monetaryValue)) {
        throw new TypeError("Invalid value passed to MonetaryUnit constructor. Expected number for property monetaryValue.");
    }
    Object.defineProperties(this, {
        monetaryValue: {
            configurable: false,
            enumerable: true,
            value: monetaryValue,
            writable: false
        },
        currency: {
            configurable: false,
            enumerable: true,
            value: options.currency,
            writable: false
        },
        currencySymbol: {
            configurable: false,
            enumerable: true,
            value: options.currencySymbol,
            writable: false
        },
    });
}
});

var UnitedStatesMonetaryUnit = MonetaryUnit.extend({
init: function(options) {
    options.currency = "USD";
    options.currencySymbol = "$";
    this._super(options);
}
});

var UnitedStatesDollarUnit = UnitedStatesMonetaryUnit.extend({
init: function() {
    this._super({monetaryValue: 1});
}
});

var UnitedStatesQuarterUnit = UnitedStatesMonetaryUnit.extend({
init: function() {
    this._super({monetaryValue: 0.25});
}
});

var UnitedStatesDimeUnit = UnitedStatesMonetaryUnit.extend({
init: function() {
    this._super({monetaryValue: 0.1});
}
});

var UnitedStatesNickelUnit = UnitedStatesMonetaryUnit.extend({
init: function() {
    this._super({monetaryValue: 0.05});
}
});

var UnitedStatesPennyUnit = UnitedStatesMonetaryUnit.extend({
init: function() {
    this._super({monetaryValue: 0.01});
}
});


var ChangeDispenserStubbed = Class.extend({
init: function() {
    // @todo
},
dispense: function() {
    // Leave this empty.
},
unitsAvailable: function(currency) {
    // @todo
}
});

if (typeof ChangeDispenser === "undefined") {
ChangeDispenser = ChangeDispenserStubbed;
}


var ChangeDispenserController = Class.extend({
init: function() {
    this.changeDispenser = new ChangeDispenser();
},
calculateChange: function(unitsTendered, price) {
    // @todo: Calculate change and dispense change.
}
});

// Unit tests
if (ChangeDispenser === ChangeDispenserStubbed) {
// @todo
}

This is what I have so far, but the wording on some things is confusing me. One is that it seems that the change dispenser stub needs a way to fail, but given the specific wording (amountTendered > price) there is no way to….

Given the above code, this is what I have done myself so far.

var ChangeDispenserStubbed = Class.extend({
   init: function() {
   this.instances = [
       // mock instances, several of these
       new UnitedStatesDollarUnit(),
       ...
   ];
   Object.defineProperties(this, {
       UnitedStatesDollars: {
           get: function() {
               var _UnitedStatesDollars = [];
               for (var i = 0; i < this.instances.length; i++) {
                   var _instance = this.instances[i];
                   if (_instance instanceof UnitedStatesDollarUnit) {
                       UnitedStatesDollars.push(_instance);
                   }
               }
               return UnitedStatesDollars;
           }
       },
       // This then occurs again for the rest of the denoms
   })
   },

   unitsAvailable: function(currency) {
      if (currency === 'USD') {
          return [{
            propertyName: 'UnitedStatesDollarUnit',
            unitInfo: {
                monetaryValue: 1,
                currency: 'USD'
            },
            count: this.UnitedStatesDollars
            // and so on in new objects for each denom
        }]
    }
})

var ChangeDispenserController = Class.extend({
    init: function() {
        this.changeDispenser = new ChangeDispenser();
    },

    calculateChange(unitsTendered, price) {
       if (unitsTendered > price) {
          return true;
       } else {
          return false;
       }
    }
 })

The units tests don’t bother me, neither does really the logistics of what I am trying to do. The problem seems fairly simple, but I think I am getting stuck on the wording. Is what I am doing representative of what is being asked? Any improvements I am overlooking because of what is getting me caught up?

Thank you.

Answers: