6 ms·
function pizza(boolean pepperoni, boolean bacon, boolean mushroom, boolean artichoke) now becomes 16 distinct functions.
by silvestrov 5y ago
function pizza(boolean pepperoni, boolean bacon, boolean mushroom, boolean artichoke)
now becomes 16 distinct functions.
- junon 5y agopizza_with_pepperoni_and_bacon_and_mushroom() Wow glad I took out those boolean params!
- flavius29663 5y agopizza is very suitable for the builder pattern: CreatePizza() .WithBacon() .With(artichoke) .Build(); or any combination of the above: CreatePizza() .WithMussroom() .Build() Even better, you can add new ingredients without changing any of the existing signatures: CreatePizza() .WithProsciuto() .WithTomatoSauce() .Build()
- stnmtn 5y agoWhat is the difference of this and doing an object paramater, similar to JS? createPizza({bacon: true, artichoke: true}); This has the same benefit you describe of being able to add parameters without altering any old call sites createPizza({prosiuto: true, tomatoSauce: true});
- flavius29663 5y agoIn my example the could matter, or not. I see this all the times. In your example, can you make the order matter? In my example, after each selection, you can limit or expand the further options.
- CRConrad 5y agoNothing to stop you from doing the same within the code of the multi-parameterized single function, is there? 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?
- flavius29663 5y agoYou just moved the problem to a different layer. 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 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.
- CRConrad 5y ago> You just moved the problem to a different layer. 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.
- fendy3002 5y agoNot OP, but it's useful for if: - something is done to object when some props is set or validation, such as .withStuffedCrust("cheese"), it'll set the internal props as crust="stuffed" and stuffContent="cheese" - it's branching. So rather than making user looking for the components or configuration themselves, library author can guide them with builder. Such as: myVehicleBuilder.withSixTires().withTrailerAttachment().attach(container) In this case withTrailerAttachment (and possibly withOpenBack or withBox) won't show up if you call withTwoTires(), and attach won't show up if you don't call withTrailerAttachment().
- peoplefromibiza 5y agonot necessarly. First of all, this function pizza(boolean pepperoni, boolean bacon, boolean mushroom, boolean artichoke) breaks down when you want to add ham, potatoes and sausages to the pizza. Secondly, you can optimize for the common case: fn pizza() # -> default pizza e.g. margherita fn pizza(list_of_ingredients) # -> your custom pizza if you we are talking of simple functions and not more complex patterns, such as piping, in Elixir I would do pizza |> add_ham |> add_mushroom |> well_done when using boolean parameters you are also passing down a lot of useless informations (a pizza with pepperoni would include 3 false just to remove the ingredients from the pizza and only one true) and confining yourself to a fixed set of options, that could possibly also represent an impossible state.
- stephenr 5y agoWhat kind of monster puts potatoes on a pizza?
- PretzelPirate 5y agoI love potatoes on pizza and order it at multiple pizza places. They add flavor and creaminess. The secret is to cook the potatoes properly and not just throw some french fries on the pizza.
- peoplefromibiza 5y agoYou don't know what you're talking about :) In the image: pizza with potatoes, a typical roman recipe https://i0.wp.com/www.puntarellarossa.it/wp/wp-content/uploads/2014/09/copertina-ok1.jpg https://i0.wp.com/www.puntarellarossa.it/wp/wp-content/uploa...
- stephenr 5y agoI don't care who invented it. It sounds fucking terrible. You might as well put some mashed potatoes in your rice and some pasta in a sandwich while you're at it. It's like someone said "what type of carbs would you like with this meal" and the answer was "yes".
- deleted 5y ago[deleted]
- dragonwriter 5y agoBut it should just be: function getPizza(toppings: Iterable<Topping>): Pizza { ... }