if Shrimp in Fillings then begin
Add(Shrimp);
Add(ShrimpOil); // So yummy together
Fillings.Remove(Jalapenos); // Don't go together
end;
if Jalapenos in Fillings then begin
Add(Jalapenos);
end;
etc. No?In your case you would end up calling your function like this: DoToppings(true, false, true, true);
Whereas in my case you would call the builder directly with (only) the params you want different than the defaults.
You could use named arguments, but that doesn't solve the problem completely. You will still have a large method signature, hard to use, harder to refactor, harder to test, error prone.
It also causes a lot of redundant code: the called usually only cares about one or 2 arguments, it's rare that you *need* to pass more. The rest of the args are filled in by defaults in the implementation. There could be elegant ways to handle the defaults, like overloading methods or just passing in defaults in the method signature. Default value are pretty bad IMO, for all the reasons above. Btw, if you *need* to pass that many arguments to a function, that is another code smell worth it's own discussion.
Multiple overloaded methods could have about the same amount of code int he implementation like the builder pattern, but they have a huge drawback: the caller cannot mix and match which arguments they want to pass in. If you have 4 arguments, there would be quite a high combination of parameters (18 possibilities? - 18 functions); Using the builder pattern you have to implement 4 methods only, and you're covering all the possible combinations the client might want. You can also limit some combinations in elegant ways right in the IDE while the developer is writing the code.
Think of FluentAssertions https://fluentassertions.com/introduction They have literally hundreds of possible assertions that are represented by object instances. You can combine them in an almost infinite number of possibilities.
Sure, there is no black and white, and depends on the language, the builder pattern is a good tool to have in the toolbox.
No, I think you're misunderstanding. Weird... Aha: My fault, sorry.
> In your case you would end up calling your function like this: DoToppings(true, false, true, true);
What?!? Heck no, that wasn't what I meant, why would you think that? [Goes repeatedly clicking "parent"] Aha, I see: Sorry, the threads and sub-threads have branched so I got confused as to where we are.
No, that wasn't what I meant at all. I got this sub-thread mixed up with sibling ones, and was talking in the context of languages with native enums and sets (roughly, Pascal and its descendants), where you do:
type PizzaFilling: (tuna, shrimp, peperoni, ham, gorgonzola, jalapeno) ; // Etc, etc...
PizzaFillings: set of PizzaFilling ;
function MakePizza(PizzaFillings);
And then the body of function MakePizza uses that set as per my GP comment.It's called not with a bunch of anonymous booleans like you wrote, but with a set of enumerated descriptively-named fillings as a parameter; say, a TakeOrder function builds this set by starting from an empty one (or perhaps tomato and cheese already in as defaults?) by the customer's specifications, and then calls
MakePizza(OrderedFillings);
> Sure, there is no black and white, and depends on the language,Yeah, I was attempting to show how the problem you mentioned doesn't exist in languages with better / saner types. Again, sorry for getting the contexts mixed up; I though that was what you were talking about too, and just didn't get.
> the builder pattern is a good tool to have in the toolbox.
Urgh, yeah, I suppose so... At least in languages where you need it, because they lack other more basic (Heh!) amenities.