Skip to content

Instantly share code, notes, and snippets.

@GuyARoss
Created June 29, 2020 06:53
Show Gist options
  • Save GuyARoss/18f18bec284fb1c629885e5143ba5e5f to your computer and use it in GitHub Desktop.
Save GuyARoss/18f18bec284fb1c629885e5143ba5e5f to your computer and use it in GitHub Desktop.
Code Review - Javascript Calculator

app.js

I might try to avoid having these global variables, lets look into why we need them.

let prevResult = null;
let currentOperation;

So it looks like we are reassigning prevResult in the executeOperation method within the Calculator class. Do we reassign elsewhere? Ah so we do it as part of our clear procedure.. so I guess the first thing I would do here is rather than just doing these reassignments globally. So maybe an approach here would be to move the prevResult global variable as a property of the Calculator class. Then using the constructor to set the default value to undefined.

So the class would end up looking like:

class Calculator {
    prevResult;
    constructor(prevResult = null) {
        this.prevResult = prevResult
    }
}

then we go to use it in the Calculator class we can just call this.prevResult. I think that we can also do the same thing with the currentOperation global variable, so rather than defining it globally, we just define it as a property in the Calculator class and set the default value within the constructor.

Some more notes about the Calculator class, I might avoid setting default values that come directly from some dependency as the DOM like on line 5

resultField = document.getElementById('result-field');

this tends to be a pretty big code "smell" (wiki) as it will now be pretty hard to use our calculator class anywhere in the future without bringing along the dependency of the DOM.

I'd like to stand back real fast and think about what the "calculator" class should be doing. Is it managing the operations? manging the dom? It should only have one purpose (Single Responsibility Principle and it should do that single thing well and extensively (Open Closed Principle).

So let's take a second, and think about what a calculator comprises of, and design our "Calculator" class around that. Also, let's just drop the DOM, for now, let's make the calculator first, and apply the dom after. So if I were to list out what a calculator does it would look something like:

  • Takes some number
  • Takes some operator
  • Takes some other number
  • Applies operator to set of numbers
  • Outputs the "operated" number

Provided this, I need something to hold my numbers and I need some operator. So let's start with something very basic, and talk about how we can refactor.

class Calculator {
    values = []
    operator = undefined

    set operator(operator) {
        this.operator = operator
    }

    set number(number) {
        // removes first number, puts in the new number 
        this.values.shift() 
        this.values.push(number)
    }

    constructor() {
        values = [0, 0]
    }

    calculate() {
        return applyOperator(operator, values)
    }
}

Where my applyOperator function looks something like:

// you could also use a switch statement here, in this case I am just using a dict or a "truth table". Really just a stylistic thing.
const applyOperator = (operator, values) => ({
    [operator === '+']: values.reduce((a, c) => a + c),
    [operator === '-']: values.reduce((a, c) => a - c),
    [operator === '/']: values.reduce((a, c) => a/c),
    [operator === '*']: values.reduce((a, c) => a * c)
}).true

So maybe something like that, really what I am trying to demonstrate here is that you want to just have your class do what it is intended to do and nothing more, with as little dependencies as possible.

I might try to follow the same pattern of being as little dependencies into your classes as a minor refactor and see what you end up with. As an addition, I think it would be easier if you split out your classes into their own files, so your program is easier to read.

@felixlevert
Copy link

Oh wow this is awesome! Wasn't expecting anything this in depth! I'm about to go to sleep but I'll refactor with your advice in mind!

The reason I had those global variables was because when I tried to define them inside the constructor of the Calculator class it would only work when I was only doing the same type of operation, as soon as I did say a subtraction after an addition it wouldn't work. I knew it was wrong, but couldn't think of a better way.

I need to dive deeper into getters and setters, I'm having trouble getting my head around them and knowing when to use them. Only briefly learned about them so far.

The applyOperator function looks quite foreign to me, so I'll do some digging to learn more about that syntax. Again I know a bit about reduce but not super comfortable with it yet.

Thanks again! This is really helpful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment