-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Eliminate GamePhaseProperty #109
Comments
@samreid said:
Looking at both Property and Emitter, searching for "order', and reading the docs for |
I added documentation to clarify that listeners are called in the order they were added. |
This has nothing to do with the order that I am inclined to close this as "won't fix". I don't really see the point in changing this because it works, and it's a totally valid approach. @samreid please comment. |
I discovered that this TODO was added (with no issue number) and is apparently related to this issue:
Yes, I could accomplish this same thing by registering a first listener to initialize things for the game state before calling other listeners. But that's a more fragile approach, and I don't see the point in introducing an order dependency (and potential maintenance issue) where there currently is no problem. So I'm not going to change this. Closing. |
Can we discuss further and possibly get a second opinion? I'm wondering if I'm the only developer who would come across that code and think it should be changed. If I understand correctly, this patch would have the same behavior, but with 28 fewer lines of code and no need for a newly introduced class.
This simulation will one day be instrumented for accessibility and PhET-iO and it seems valuable to make sure it is in a streamlined state. Introducing a new Property subclass for something that is already supported by axon feels like an anti-pattern.
Taking this to its logical conclusion, are you suggesting that any time we need to enforce a particular order of notifications, we would introduce a subclass that hard-codes the required order?
GamePhaseProperty would require maintenance. For instance, at the moment it does not match the signature of the method it overrides and IDEA highlights it as such: Is your primary concern is that one day AXON will change the order of notifications? Or that another listener will "sneak" in before this one? If the former, would a qunit test that checks for callback order help? If the latter, would an assertion or graphing-lines qunit test help? |
That's fine with me. I'd also like to understand why you think this is important.
You have introduced an order dependency (a code smell) that I prefer not to have in my code. Please explain why having an order dependency is preferable to my solution that has no ordering dependency.
For subclassing Property in general... If you're inferring that we should have no sim-specific subclasses of Property to "streamline" PhET-iO, I strongly disagree. PhET-iO should be able to easily accommodate subclasses of Property. For GamePhaseProperty specifically... I doesn't see why overriding
No. I'm saying (1) don't write code that has order dependencies, and (2) don't change code that doesn't have a problem. In this case, it made sense to encapsulate the "setup" work for a game-state change in GameStateProperty (and this in fact may have been ported directly from Java). I don't see any advantage (PhET-iO or otherwise) to changing it.
Thanks for pointing that out. I should have been returning the Property, and it's fixed in the above commit. Good to address this, but I don't see how it invalidates the approach of subclassing Property or would have affected PhET-iO operation.
No, that's not my primary concern. My primary concern is writing code with order dependency, the real anti-pattern here, which is what you're promoting. For this specific case... Even if AXON's order of notification never changes, there is no support for guaranteeing that the "first listener" in your patch is (and remains) first. Future changes/maintenance could add an earlier listener, breaking the order dependency. In general... The Observer pattern does not specify order of notification. java.util.Observable in fact specifies that "the order in which notifications will be delivered is unspecified". And writing code that relies on order is generally a bad practice. Order of notifications for Property and Emitter was unspecified until you decided otherwise in phetsims/axon@1239bb6. Against my better judgement, I gave my blessing to that, but it didn't receive general discussion. So up until this point, I have been writing code based on the Observer pattern, and have not been relying on order of notification. Regardless of what you've decide for Property and Emitter, I plan to continue that practice. |
It seems nice to eliminate 28 lines of code. Less to maintain, understand, instrument, etc. Less places for bugs to creep in.
Should we go through all the rest of PhET's code and identify and eliminate order dependencies? Should we add code review checklist entries for it? Do you think other PhET code has buggy order dependencies?
It seems nice to eliminate 28 lines of code. Less to maintain, understand, instrument, etc. Less places for bugs to creep in.
The code runs correctly, that is not the problem. The problem is that it has 28 extra lines of code to manage an order dependency. That's a maintenance and understandability problem.
You don't see any advantage to removing 28 lines of code?
The point is that there is extra code that needs to be read, understood and maintained.
If it is sometimes important to manage order of callbacks, perhaps AXON should support this directly instead of managing a one-off solution in a simulation?
In #109 (comment) I proposed using assertions for guaranteeing that the listener remains first. Do you think that wouldn't work for some reason? |
I would guess that there are order dependencies in PhET code, and they may or may not have manifested as bugs. I'm not interested in spending my time proactively identifying them. Avoiding ordering dependencies is a best practice that I thought every programmer was familiar with. If you think it would be valuable to explicitly state that in the code-review checklist, feel free to add it.
I don't understand why you think that introducing an ordering dependency (a cardinal sin of programming where I come from) is acceptable in order to eliminate a mere 28 lines of working code that is trivial to understand, well documented, has no known problems, and no ordering dependency. But since you said "28 lines of code" 4 times in the above comment, I guess you think that's more important.
You proposed a qunit test or assertion. Both of those add complication (and lines of code, reducing your 28-lines savings), and are imo inferior to the current solution. |
If it is sometimes important to manage order of callbacks, perhaps AXON should support this directly instead of managing a one-off solution in a simulation? |
That sounds like undesirable complication in AXON, it may encourage using expedient-but-inferior solutions, and I think it's preferable to find solutions that don't require order dependencies. But if you want to write code that requires order dependencies, then you'll probably need something in AXON to make that robust. |
Graphing Lines apparently requires that the What would go wrong if |
I would wait until there is a general need. One instance is hardly general, and it seems like we're spending a strange amount of time on this, given all of the other more high-priority tasks that are not completed.
I may be wrong... But I seriously doubt that my past self would have done this if there hadn't been a legitimate need to do setup before |
I see at least one error that will be introduced if |
Another way to eliminate GamePhaseProperty would be: // @public (read-only) set this via setGamePhase
this.gamePhaseProperty = new Property( GamePhase.SETTINGS );
/**
* Sets the game phase. Call this instead of setting gamePhaseProperty directly,
* because there is setup that needs to be done before listeners are notified.
* @param {GamePhase} gamePhase
*/
setGamePhase: function( gamePhase ) {
// do the setup that's currently done in hook, then...
this.gamePhaseProperty.set( gamePhase );
} ... and change all occurrences of |
The proposal in the preceding comment seems preferable to me. Extending Property to add something that is like a listener but not a listener seems odd. It also had odd semantics that Some possible ways to proceed:
To me, 7 makes the most sense as a compromise that reaches many of our shared objectives. I'm also happy with 6 or 3. |
Here's the patch for the solution I described in the previous commit. It seems to work, but it will take me at least 30 minutes to properly regression test. That would make close to 2 hours that I've spent on what is imo a non-issue.
|
I'm going to apply the above patch and test. |
@samreid said:
I couldn't disagree more. This is the reason that methods are overridden and chained to the superclass - to extend a method's tasks with things that are done before and/or after the superclass' tasks. It is a core pattern in OO programming. And this is the pattern that was used in GamePhaseProperty. |
I think this issue can be closed after QA has verified it. UPDATE: Please also see my relevant notes in phetsims/gravity-and-orbits#276 (comment) |
Before QA tests this, should we discuss whether |
Yes, I could revert everything I've changed here and use @ariel-phet your opinion? |
I believe we have thus far only discussed and implemented
I'm fine with any of the proposed solutions, including the original solution. The different solutions have minor pros and cons but they don't seem too important. Up to you or @ariel-phet how to proceed. |
I see no clear benefit to making any more changes. Let's continue with the RC as is. |
Agreed. QA, please proceed. And if it's not clear what needs to be done to verify this issue... Please give the entire game screen, in both Graphing Lines and Graphing Slope-Intercept, a thorough test on 1 platform, and spot check on all platforms. There were no platform-specific changes here, so any collateral damage would be universal. |
Haven't seen any problems here in either RC. Closing |
Discovered in phetsims/axon#213, BaseGameModel has this inner class:
It seems the whole purpose of this class is making sure one listener is called before the other listeners are called. Axon Property and Emitter guarantee that listeners will be invoked in the order they are added and there is no API for changing the ordering of listeners. Therefore, a better solution would be to
link
orlazyLink
a listener directly to thethis.gamePhaseProperty
and create it as aProperty
rather than introducing a new subtype for this.The text was updated successfully, but these errors were encountered: