In my experience, creating the function hurts readability and provides no benefit.
This is only because the function is extremely short, and because it doesn't provide abstraction.
If the function was longer, or more abstract than the code inside it, then I would not be against adding the function.
My opinion on your C# example is the same. You have refactored this:
... var redFord = cars.Where(x => x.Brand = "Ford").First(x => x.Colour = "Red"); var redToyota = cars.Where(x => x.Brand = "Toyota").First(x => x.Colour = "Red"); ...
into this:
... var redFord = FindBrandedRedCar(cars, "Ford"); var redToyota = FindBrandedRedCar(cars, "Toyota"); ...
But why red? The next line might be:
var blueHonda = cars.Where(x => x.Brand = "Honda").First(x => x.Colour = "Blue");
and we can't refactor this into a call to FindBrandedRedCar
because we're looking for a blue car. We could create a FindBrandedColouredCar
that takes a brand and a colour as arguments:
var redFord = FindBrandedColouredCar(cars, "Ford", "Red"); var redToyota = FindBrandedColouredCar(cars, "Toyota", "Red"); var blueHonda = FindBrandedColouredCar(cars, "Honda", "Blue");
But what if you want to search for a different criteria? All Toyota station wagons? You can't do that with FindBrandedColouredCar
, so let's generalize it to FindCarByFields
:
var redFord = FindCarByFields(cars, "Brand", "Ford", "Colour", "Red"); var redToyota = FindCarByFields(cars, "Brand", "Toyota", "Colour", "Red"); var blueHonda = FindCarByFields(cars, "Brand", "Honda", "Colour", "Blue"); var toyotaStationWagon = FindCarByFields(cars, "Brand", "Toyota", "Style", "StationWagon");
But how do we find a car with an engine displacement less than 1.5 litres? We can't pass "Engine.TotalDisplacement"
to FindCarByFields
because it's not a field. So instead, let's make it so you can pass a lambda:
var redFord = FindCarByLambda(cars, x => x.Brand == "Ford" && x.Colour == "Red"); var redToyota = FindCarByLambda(cars, x => x.Brand == "Toyota" && x.Colour == "Red"); var blueHonda = FindCarByLambda(cars, x => x.Brand == "Honda" && x.Colour == "Blue"); var toyotaStationWagon = FindCarByLambda(cars, x => x.Brand == "Toyota" && x.Style == "StationWagon"); var smallEngineCar = FindCarByLambda(cars, x => x.Engine.TotalDisplacementLitres < 1.5);
You know, .NET actually has this method already. It's called First
:
var redFord = cars.First(x => x.Brand == "Ford" && x.Colour == "Red"); var redToyota = cars.First(x => x.Brand == "Toyota" && x.Colour == "Red"); var blueHonda = cars.First(x => x.Brand == "Honda" && x.Colour == "Blue"); var toyotaStationWagon = cars.First(x => x.Brand == "Toyota" && x.Style == "StationWagon"); var smallEngineCar = cars.First(x => x.Engine.TotalDisplacementLitres < 1.5);
Oh look, we're right back where we started. Only extract functions if they are actually useful somehow. There are lots of different ways that it can be useful to extract a function, but hiding this sort of trivial duplication is not one of them.
Going back to the Javascript example, you have this:
var foo = getKendoDropdown(window.foo); var bar = getKendoDropdown(window.foo); var sna = getKendoDropdown(window.sna); var fu = getKendoDropdown(window.fu);
There's still duplication here! Wouldn't it be much better to write the code like this?
... var kendoDropdownLists = getKendoDropdownLists(["foo", "bar", "sna", "fu"]); ... function getKendoDropdownLists(controlIds) { var result = {}; for (var k = 0; k < controlIds.length; k++) { // pardon me not being up to date with modern JavaScript practices { var controlId = controlIds[k]; result[controlId] = $('#' + controlId).data("kendoDropDownList"); } return result; } ...
Look mom, no duplication at all! But is it better? I don't think so.