Wouldn’t this be memory inefficient because you’re returning a new object through every builder method instead of modifying the existing builder using the "this" keyword?
@@azizsafudin what a little man thought... "Let's not strive to do the best code possible simply because the language has bad memory management." It's as if JavaScript's memory management issues come from developers writing inefficient code, forcing engine developers to patch the problem at the engine level. Imagine if the engine guys could dedicate their efforts solely to addressing genuine memory problems instead of compensating for inefficient coding practices. Jesus...
If there are no references to the object that is being copied then it should be garbage collected. The cost of copying is negligible, assuming you aren't using a deeply nested object or have properties that have a lot of memory associated with it (might be an indicator of bad design!). If you are still concerned about memory inefficiency, I'd suggest researching more into immutability, copy-on-write, garbage collection, and memory optimizations done by javascript engines like V8.
I've encountered many bugs caused by fluent interfaces that mutate the original object, where some other code is holding a reference to the original and it is unexpectedly changed. I have never encountered a memory problem due to fluent interfaces that create a new object on every change. I'll take "eliminate an entire class of bugs" over "eliminate some potential inefficiencies that have never been a real problem".
You could make the build fn return an Option or an Either and manually check that the object has been constructed correctly. Thought this requires more code and thus is more work, it's a better alternative when there are business rules that control the build output and you want to return a Left value with one or more appropriate errors resulting from business rules validations. This is important in situations where users must be given feedback on the results of their exact actions so that they can correct or adjust, but most importantly understand what they have to do. This is also the only way to do the build fn correctly when the actual building is dynamic and can't be hard coded, such as when the instructions (build recipe) are let's say received over the wire, or from user input.
I used to throw errors from build method if something wasn't right. But seeing it right at the type level is a very nice convenience. One question I have is, why return a new object from each method? Wouldn't it be a problem if something relied on the builder's reference being stable?
The issue here is that the pattern doesn't fit the usecase, because the type allows for the missing fields to not be populated. If you have missing properties then build should not even be defined. You can fix this by using a different type that defines build and return it only hen the minimum requirements to prduce an invoice are set
Great video! Could you not initialise actual with default arguments (0 for tax rate) and leave the responsibility of validating the result of the build function to the caller if the appropriate setters aren't called?
you can, but it is worse for DX, you wont know until you run your code and it throws an error when building. I used to do exactly that because i didnt know you could use this as parameters.
I feel like a builder should never be able to be put into a state where it can't build. For example, JQuery required you at least provide a selector to the first element in the chain. Having mandator links in the chain sort of defeats most of the benefits of chaining.
Honestly I would rather have a function that takes an object that has all of these fields. Probably a query builder is a better use case, but generally this seems unnecessary.
Why creating another builder object instead of something like this ``` setFoo(foo: string) { this.#actual = { ...this.#actual, foo } return this } ``` Or maybe simply like the following. Much more efficient i guess ``` this.#actual['foo'] = foo return this ```
Holy cow! I didn't know that trick to type "this" as a parameter
damn same, i used to validate it with zod, but i like this approach better.
@@Nicholas-qy5bu validation + type check is the way to go.
@ why would you validate ? If you are in strict mode there should be no need right ?
Wouldn’t this be memory inefficient because you’re returning a new object through every builder method instead of modifying the existing builder using the "this" keyword?
It is, but in the grand scheme of how bad JavaScript is with memory management, it doesn’t really matter
@@azizsafudin what a little man thought... "Let's not strive to do the best code possible simply because the language has bad memory management." It's as if JavaScript's memory management issues come from developers writing inefficient code, forcing engine developers to patch the problem at the engine level. Imagine if the engine guys could dedicate their efforts solely to addressing genuine memory problems instead of compensating for inefficient coding practices. Jesus...
If there are no references to the object that is being copied then it should be garbage collected. The cost of copying is negligible, assuming you aren't using a deeply nested object or have properties that have a lot of memory associated with it (might be an indicator of bad design!). If you are still concerned about memory inefficiency, I'd suggest researching more into immutability, copy-on-write, garbage collection, and memory optimizations done by javascript engines like V8.
I've encountered many bugs caused by fluent interfaces that mutate the original object, where some other code is holding a reference to the original and it is unexpectedly changed.
I have never encountered a memory problem due to fluent interfaces that create a new object on every change.
I'll take "eliminate an entire class of bugs" over "eliminate some potential inefficiencies that have never been a real problem".
@@Guergeiro what a little man comment... if you have memory constraints, don't use javascript!
You could make the build fn return an Option or an Either and manually check that the object has been constructed correctly. Thought this requires more code and thus is more work, it's a better alternative when there are business rules that control the build output and you want to return a Left value with one or more appropriate errors resulting from business rules validations. This is important in situations where users must be given feedback on the results of their exact actions so that they can correct or adjust, but most importantly understand what they have to do. This is also the only way to do the build fn correctly when the actual building is dynamic and can't be hard coded, such as when the instructions (build recipe) are let's say received over the wire, or from user input.
Or just yeet an exception
Unlike rust comma, we don’t have access to the build state pattern with compile safety as strong, but I think that is my favorite builder pattern
Thanks for the great video!
Always looking forward to your contents
I wonder how you feel about Luster (Frontend Framework using Gleam)?
Hi Andrew, I love to see strategy patterns in TS using a real working example.
I used to throw errors from build method if something wasn't right. But seeing it right at the type level is a very nice convenience.
One question I have is, why return a new object from each method? Wouldn't it be a problem if something relied on the builder's reference being stable?
The issue here is that the pattern doesn't fit the usecase, because the type allows for the missing fields to not be populated. If you have missing properties then build should not even be defined. You can fix this by using a different type that defines build and return it only hen the minimum requirements to prduce an invoice are set
That would be ideal! Do you have an example of doing this well in TS? I'm not sure how you would strongly-type that...
Great video! Could you not initialise actual with default arguments (0 for tax rate) and leave the responsibility of validating the result of the build function to the caller if the appropriate setters aren't called?
you can, but it is worse for DX, you wont know until you run your code and it throws an error when building. I used to do exactly that because i didnt know you could use this as parameters.
which font is this please?
Mono Lisa
Is that first argument required to be named this? That trick is neat
i love the builder pattern from java and rust, but it's pretty painful with javascript. a lot of things are painful with javascript.
You should link your blog posts when u mention it
Ah good call! I usually do, but forgot this time. Updated the description with the link, thanks!
I feel like a builder should never be able to be put into a state where it can't build. For example, JQuery required you at least provide a selector to the first element in the chain. Having mandator links in the chain sort of defeats most of the benefits of chaining.
great video!
Honestly I would rather have a function that takes an object that has all of these fields.
Probably a query builder is a better use case, but generally this seems unnecessary.
Why creating another builder object instead of something like this
```
setFoo(foo: string) {
this.#actual = {
...this.#actual,
foo
}
return this
}
```
Or maybe simply like the following. Much more efficient i guess
```
this.#actual['foo'] = foo
return this
```
1:52 It reminds a monad
🔥🔥🔥
Nice, but I’m just here to track the progress of your facial hair. 😅