How to avoid duplicate if checks when overriding a method in JavaScript?
I have the following parent class method in JavaScript:
class Parent {
jump() {
if (!this.isDead()) {
this.lastActivityTime = Date.now();
}
}
isDead() {
// returns a boolean
}
}
In my child class, I'm overriding the jump() method like this:
class Child extends Parent {
jump() {
if (!super.isDead()) {
super.jump();
this.speedY = 30;
}
}
}
I have a duplicate if check for isDead(): once in the parent class and once in the child class. How can I refactor the code to avoid repeating this check, while still maintaining functionality and ensuring the parent method is called correctly?
What is the best practice for handling such situations in JavaScript to make the code cleaner and more maintainable?19 Replies
It seems like you are trying to overoptimize. Anything you can do to avoid the duplicate check will make the code more complex and will not improve efficiency at all. If you insist on still doing this, I can think of two ways to do it:
1) Return a boolean from jump() to indicate if the jump happened. The Child can then call super.jump() unconditionally and set speedY only if that call returns true.
2) Make jump() accept a callback that it calls along with setting the value of this.lastActivityTime.
I advise against using either approach, and I doubt any other creative solutions will actually improve efficiency beyond what you already have.
I just realized a possible third option. You could call super.jump() unconditionally AND set speedY unconditionally if the new value of speedY has no effect on a dead entity. This still does not seem better than what you have.
agreed, this sounds like overoptimization as long as that isDead() call is simple enough. If it's just a
return this.dead
or return this.hp <= 0
or whatever, just call it as much as you wantOnce
dead
is true, it's only getting called once in any case. super.jump()
never gets called in the child when the call returns negative in the parent.
The only alternative I could see would be to throw an error since you want execution to essentially halt if dead
. That, or use guard clauses like:
Those are just to reduce the amount of nesting though, not to reduce the number of calls.the question is: how do you check that it is dead?
how much work do you have to do?
I think this is the best solution. But the if-check is not needed in the child class.
Please correct me if I'm reading this incorrectly, but it seems to me that you're solving the wrong problem. Based on the code snippet provided, at least, the only reason to invoke the parent's
jump
method is to set the last activity time. First of all, I would consider the easiest and most straight forward solution to just "duplicate" the jump method on the children, with the added portion to update the speed.
I would argue that this tiny bit of duplication is worth it... in fact, I would argue this isn't duplicated code at all, even though it might seem like it. What "duplicated code" really refers to are sections of code that achieve the same goal within the same context. Two functions, or whatever, that are doing the same thing for different reasons are not duplicated code, since one could be updated at any time without affecting the other.
In this case, the parent jump
method logs the activity but the child's jump
method also updates the speedY. If you really need to do the same thing, why bother overwriting the method? That's the entire point of inheritance, take advantage of it. As an example of how you might want to change the child's jump
method, you might want to add additional checks such as isCrawling
, isSwimming
, isFlying
... since not all instances of child might have any or all of those properties, it's clear that the jump
method should be independent from the parent. Therefore, not duplicated code.
That all said, if you really want to have the activity logged whenever you update the speed (and probably other properties) consider using getters/setters:
Notice how doing this you don't even need a jump method on the parent anymore. It's nice and concise, although this may lead to challenged in either testing or debugging since now you have a single action doing multiple things: use sparingly and with caution. Or, simply "duplicate" code. As long as you understand when it really is duplicate and when it isn't.
Also note, I'm using private properties here not only for correctness, but also because it allows using the same property name for the getter/setter. Otherwise you would have to do _speedY, etc.dont use
Date.now()
for this, as it has intentional reduced precision
for example, firefox is intentionally imprecise to 2ms, but can be set to 100ms
use performance.now()
as it is an high precision timer
besides, performance.now()
doesnt just return milliseconds, but goes lower (microseconds, i think - cant verify that)
yes, it's supposed to be accurate to 5 microsecondsI suspect that the millisecond variant in this case is irrelevant.
yes, but
Date.now()
can go back in time when the system clock is adjusted automatically with ntp (built into windows)
performance.now()
always moved forwardI'd use
new Date()
for semantic reasons.so, you use the "wrong" tool because of semantics?
Why is it wrong? You don't know the context. If the requirement is to keep a log of when a component was last active, you don't want to use relative times.
Use whatever works best and worry about performance when you ahve a reason to care about performance.
it's not about performance
it's about
Date.now()
being unsuitable for this type of thing
it doesn't have enough precision and it can skip back and forwards in time
that type of unexpected behaviour can really mess up a game, which is what looks like the code is forAgain, you don't know what the context is so you don't know whether it is suitable or not. A few milliseconds in variance might also be perfectly acceptable for this game.
it doesn't have enough precision and it can skip back and forwards in timeYou can account for that by other means...
im not talking about a few milliseconds
but anyway, this isnt' even what this thread is about!
this is about the jump function
i know
if it's being used to track jumping and stuff, it can cause really weird behavior
it's a valid thing to point out, given how odd the bugs caused by inconsistent timing in video game logic can be
Sure but we don't know that based on the question asked. There are other things that could be pointed out that will get us off-topic for no reason...