Add zeroth draft of split-loop-with-lambda refactoring pattern
Joint work with Emily Bache, based on the IceCreamForecasting-Refactoring-Kata. Names the mechanics (introduce lambda -> slide statement -> extract method taking the lambda) used when plain Split Loop stalls because the variable to slide is a function, not a value.
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# Authors: Willem van den Ende, Emily Bache
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
For a client, Emily and Willem have done several Split Loop refactorings. We want to be explicit in naming the patterns, and ensuring that the team have sufficient memory to complete these on their own. In the final session we did Split Loop, but the type of variable that we did the 'slide statement' refactoring with was a function instead of a value. And the 'split' part of the lop that got sliced up was an iteration that resulted in a list of function invocations (if memory serves me write).
|
||||||
|
|
||||||
|
This is a zeroth draft. Our meta-goals are being more explicit in naming the refactorings and patterns we used to ourselves, so that the team can name the patterns, and reproduce the mechanics reliably on their own.
|
||||||
|
|
||||||
|
Initial literature survey (see related patterns below) has not yet revealed an existing Refactoring Pattern description. In order to fit some or all of this refactoring in a learning hour, it is worth writing down what we mean, with examples (so, in case the resulting exercise does not fit in a learning hour, we have a blog post to refer to for more details and/or recapturing the mechanics).
|
||||||
|
|
||||||
|
# context / motivation
|
||||||
|
|
||||||
|
you have a long method. Somewhere in the mindle of the method, a collection of values is being transformed. the transformation depends on an object with multiple dependencies, and/or you do not want that particular collection in the composed method (it stands in the way of composing the method).You want to move this method call) to another class, but do not want the dependencies there.
|
||||||
|
you can not " extract parameter" ,because multiple values are transformed,and if the transformation needs to be done depends on the decisions inside the method. (A lambda introduces an explicit form of laziness. Instead of a value, we inject a function that can get the value, when needed. Instead of calculating the value beforehand, and then discard it when not needed).
|
||||||
|
|
||||||
|
# **Mechanics**
|
||||||
|
|
||||||
|
We use some of the mechanics from the [Split Loop](https://www.sammancoaching.org/refactorings/split_loop.html) refactoring, even though the immediate motivation is not Divergent Change. The inside loop may conceptually do one thing, but depend on something outside of the current method that we do not want (side-effects, or concerns that are not properly separated yet).
|
||||||
|
|
||||||
|
Split loop suggests “‘introduce variable’ refactorings and ‘slide statement’ “. These may be necessary, but the introduced variable is of type function, not value.
|
||||||
|
|
||||||
|
1. Extract the code that uses the unwanted dependency into a lambda
|
||||||
|
2. Use slide statement on the definition of the lambda to the top of the method. Leave the invocation in place
|
||||||
|
3. Extract the method below the slides statement. You now have a new method, that takes the lambda as a parameter
|
||||||
|
4. Move the method to another class where the unwanted dependency is not present
|
||||||
|
5. Move and/or create tests, using the lambda as a stub instead of a concrete invocation of the unwanted dependency.
|
||||||
|
6. Check that “everything that could possibly break” is tested \- the extracted lambda allows more combinations to be easily tested.
|
||||||
|
|
||||||
|
# Consequences / resulting context.
|
||||||
|
|
||||||
|
focused part of a method that can be
|
||||||
|
extracted to form a composed method, or moved.
|
||||||
|
|
||||||
|
the Lambda may still have side-effects in production code, it is not purely functional..
|
||||||
|
the code, method, is now testable. the "implementation"in the tests is a pure function, or a list ofpure functions.
|
||||||
|
Be mindful of a bunch of lambdas lying around. New design emerge from there. this might lead to extracting ports. Alambda function like this is a tiny adapter.
|
||||||
|
|
||||||
|
# Aha moment for exercise
|
||||||
|
|
||||||
|
the new tests are actually glanceable and pure.
|
||||||
|
A pure function has no side-effents (e-g. state or IO)
|
||||||
|
|
||||||
|
**Related patterns**
|
||||||
|
extract function pointer
|
||||||
|
extract method object- not sue if this exists
|
||||||
|
method obic is in case a sunde amount ofState is needed.
|
||||||
|
Adapter \- we adapt on interface that does notfit, to what we need. I
|
||||||
|
Osterhout: small interface that hides complexitygood power-to-weight ratio 1 1 11
|
||||||
|
|
||||||
|
[https://www.sammancoaching.org/learning\_hours/testable\_design/slice.html](https://www.sammancoaching.org/learning_hours/testable_design/slice.html) slice learning hour that has a graphic of the movement, but doesn't describe the exact mechanics we use here.
|
||||||
|
![][image1]
|
||||||
|
|
||||||
|
Not for public consumption: There is a video of Emily Bache demoing the [Icecream Slice Refactoring](https://www.dropbox.com/scl/fi/wu7df9xiqzpk0b6p7egid/icecream_slice_java.mp4?rlkey=usz1ggkje0ovhahin0jco0u1u&st=knuwpl1b&e=1&dl=0) where she performs this exact refactoring \- extracting a lambda, using the function as a parameter to an extracted method, and using a pure lambda implementation of the function to create/fix an approval test. Kata: [https://github.com/emilybache/IceCreamForecasting-Refactoring-Kata](https://github.com/emilybache/IceCreamForecasting-Refactoring-Kata) . the video focuses on the LongRangeForecast method in java.
|
||||||
|
|
||||||
|
Extracting a lambda, and using its’ type can be seen as a tiny implementation of [https://martinfowler.com/bliki/RoleInterface.html](https://martinfowler.com/bliki/RoleInterface.html) for a micro-adapter. We have one function signature (the Role Interface) and one small implementation. It is, in a way, the smallest Port (function signature) and Adapter (the extracted code with the unwanted dependency) that could possibly work. Later on, we might discover that some state and behaviour of the unwanted dependency can be grouped together in a larger Adapter, hidden behind a number of smaller RoleInterfaces (e.g. the read side and the write side of a Repository implementing the CQRS pattern by exposing separate interfaces for the Command and Query side, used by different clients).
|
||||||
|
|
||||||
|
# Aside
|
||||||
|
|
||||||
|
Motivating quote from "refactoring to patterns" by Joshua Kerievsky, page 123, "compose method":
|
||||||
|
"Do you write a lot of Composed Methods? I like to think I do, but I often find that I don't, at first. So I have to go back and refactor to this pattern. When my code has many Composed Methods, it tends to be easy to use, read and extend "
|
||||||
|
|
||||||
|
JK also quotes Kent Beck, he wrote Composed Method in "Smalltalk Best Practice Patterns" and thought it was too simple to write down, but was appreciated by many afterwards.
|
||||||
Reference in New Issue
Block a user