Hacker News new | ask | show | jobs
by treve 1740 days ago
Religiously splitting functions with boolean arguments doesn't always result in more maintainable code.

Instead of trigonometry functions, how would you refactor JS's fetch() with many of its behaviour-altering flags?

2 comments

> how would you refactor JS's fetch()

as @flavius29663 said (https://news.ycombinator.com/item?id=28593669) you can use the builder pattern

    FetchBuilder()
      .withUrl(ur)
      .withMode("cors")
      .withCache(true)
      .withHeader('Content-Type', 'application/json')
      .accept('*/*')
      .post()
      .then(response => response.json())
      .then(data => console.log(data));
I'm not following how moving the options out of the function parameters and into the call chain makes the actual function more maintainable. It's still doing the exact same thing with the exact same options it's just pulling them from elsewhere. If anything you now have more functions to maintain on top of the function that does many different things based on the calling info.

    // The original "misses the point"
    trig(mode="cos", type="hyperbolic")
    
    // The style fetch() uses today
    trig({mode: "cos", type: "hyperbolic"})
    
    // The builder refactor
    trigBuilder().withMode("Cos").withType("hyperbolic").calculate()
Personally I don't like using with prefix in builders, I was simply presenting an example from another comment.

the difference, IMO, is - in Javascript - that this

    // The style fetch() uses today
    trig({mode: "cos", typ: "hyperbolic"})
                       ^^^
would fail silently while

    // The builder refactor
    trig().mode("cos").typ("hyperbolic")(val)
would trigger a compilation error

but, IMO, passing objects is good enough most of the times, and I consider it a much better solution over passing boolean flags

I see the builder pattern as a way to manage lack of keyword arguments. I see very little difference between your example and the actual fetch API that takes an object as JS's version of keyword arguments.

Languages with good support for named/keyword arguments have more features such as required arguments and preventing duplicate arguments. With builder patterns your only real option is to make the builder constructor have required arguments (or throw a runtime error upon finalizing the builder).

> . I see very little difference between your example and the actual fetch API that takes an object as JS's version

true

the only difference is in the tooling

code completion for methods names works much better than autocompletion for objects' fields.

And you can't mistype a method name, it would not run and give you back a - hopefully - meaningful error, while the same is not true for objects' fields.

> code completion for methods names works much better than autocompletion for objects' fields.

That is true for vanilla JS, unknown parameters will be ignored and unset will be set to undefined. However for languages that support keyword arguments (or even TypeScript[1]) the tooling should be even better than for the keyword argument case.

[1] https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABMO...

Good rules of thumbs are given in the "Deciding which to use" section of the article. For the fetch() function, I'd keep most parameters as is since they don't change the essense of the function. But "cache" and "redirect" do (following redirects can cause N http requests rather than just one and using the cache perhaps 0) so I'd refactor them as new functions. Imagine adding retry functionality to the fetch() function using parameters. I think you can see how this leads to feature creep.