exercism / javascript

Exercism exercises in JavaScript.
https://exercism.org/tracks/javascript
MIT License
574 stars 614 forks source link

Windowing System: Improvement Ideas #1600

Open junedev opened 2 years ago

junedev commented 2 years ago

After seeing a couple of solutions to "Windowing System" during mentoring, I think the exercise could need some changes to better achieve it's learning objectives.

Some ideas how to improve the exercise:

SleeplessByte commented 2 years ago

(Maybe related: https://javascript.info/private-protected-properties-methods)

Since this is a concept exercise, we can add some tests to remove some of this behaviour, if you'd like. Technically we could also start pushing the #private syntax (even though I hate it) given its current stage in the TC39 process.

junedev commented 2 years ago

I read a lot of negative posts about the real private properties lately so I don't feel like pushing them either. Also I prefer to stick to stage 4 as much as possible in the concepts.

junedev commented 2 years ago

I would love to fix the exercise but I am not sure how to best approach this. I don't think it would be good to just make the tests enforce the use of the methods instead of direct assignment if there is no actual difference. It makes it very hard to argue the case in mentoring. I would prefer if it would be clear from the type of the problem that the method needs to be used, that some property needs to be "private", etc.

I am happy to make bigger changes to the tasks and story if someone has an idea how to construct the exercise in a way that a desired solution comes more naturally.

Here the minimum I would like to get out of the exercise:

The current instructions can be found here: https://exercism.org/tracks/javascript/exercises/windowing-system

Can anyone help with this? I have never worked in a real OOP style code base so it is hard for me to construct a sensible example here.

SleeplessByte commented 2 years ago

Ok. So. I solved the exercise @junedev , without looking at the exemplar, but purely reading the introduction and then following the instructions. This is what I came up with: https://exercism.org/tracks/javascript/exercises/windowing-system/solutions/SleeplessByte.

It is pretty close to the exemplar, so I think that if you know what you're doing, it's not a convoluted exercise at all. I do think the weakness in the exercises are as follows:

I have a few thoughts right now, but I will likely have more later this week. Most importantly, whatever we do to resolve your list of issues, I think we should not introduce inheritance, because we do not need it to solve these issues.

To show the power of the prototype we can introduce a story element that's something like:

You're building a modding system for the windows. The modding system will, when
activated, change _all_ existing windows and _future_ windows by adding 
sparkles ✨. Implement a function `activateSparkles` that:

- Defines a property on all program windows called `sparkling` and set it to true.
- Defines a method on all program windows called `safeForWork()` which turns off 
  the sparkles and resizes the window to full screen.

This will force the student to use ProgramWindow.prototype:

export function activateSparkles() {
  ProgramWindow.prototype.sparkles = true

  ProgramWindow.prototype.safeForWork = function safeForWork() {
    this.sparkles = false

    this.move(new Position())
    this.resize(this.screenSize)
  }
}

We would test that a window created in the test is also affected when calling activateSparkles.

// Initially windows are unmodded
const testWindow = new ProgramWindow()
expect(testWindow.sparkles).toBeUndefined()

// When modded, all windows, including already existing ones become sparkling
activateSparkles()
expect(testWindow.sparkles).toBeTrue()

// Open a brand new window
const brandNewWindow = new ProgramWindow()
brandNewWindow.move(200, 200)
brandNewWindow.resize(300, 300)

expect(brandNewWindow.sparkles).toBeTrue()

// Make it safe for work
brandNewWindow.safeForWork()

expect(brandNewWindow .sparkles).toBeFalse()
expect(brandNewWindow.size.width).toBe(800)
expect(brandNewWindow.size.height).toBe(600)

// The old window is still sparkling
expect(testWindow.sparkles).toBeTrue()

Note that I kept the exercise intact, just to show how it would be done in this version of the exercise.

We can further show the power of instance vs prototype separation by adding "bugfixes" to the story.

There is a window that misbehaves. When it is shown full screen, it actually only
fills half the screen, because of a drawing mistake. 

Write a function `patchSize(buggedWindow)` that will always double the width
of the `buggedWindow`, without affecting any other windows.

Depending on how we change the exercise, the student will need to overwrite the function on the instance / overwrite the property on the instance.

(A follow up excercise that deals with getters/setters etc. could then guard against this with Object.defineProperty and making a property non-configurable, or stuff like that. Cool shit).

These are my thoughts thus far. Sidenote: if we want people to not use properties, then we should have side-effects that are repeated, so you'll want to use the method.