Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ name: build_and_test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ We would like to do some refactoring. This is the plan

1. Add search feature
- [x] bring back nice XML-based test reports in GitHub workflow
- [ ] search results include unwanted pages (e.g. 404)
- [ ] ensure the teardown kills the server
- [x] search results include unwanted pages (e.g. 404)
- [x] ensure the teardown kills the server
- [x] add more pages to the search index
- [ ] remove signup thankyou pages from search index
- [ ] toggle search on in production
1. Think of a better name for "Activities" - "Learning Segments" and move it under "Reference" which we can rename to "Resources"
2. Work out what the 'workshops' collection is and whether to keep it and/or add it to the search index
3. Improve page names for open space signups - too many pages with similar names!
1. switch from scripts to rake
2. Add a field to katas for "difficulty" and allow people to sort them. Possibly tags too?
3. Show a calendar of all upcoming events. Do not show joining info, link to information page for the event. Not sure how to achieve this - base it on the google calendar?
Expand Down
5 changes: 5 additions & 0 deletions _data/contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,8 @@ devill:
url: https://www.ivettordog.com/
affiliation: Independent
member: yes
nicolerauch:
title: Nicole Rauch
url: http://nicole-rauch.de
affiliation: Independent
member: no
7 changes: 5 additions & 2 deletions _layouts/refactoring.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ <h2>Sources</h2>
{% if page.source == 'Martin Fowler' %}
<li>This refactoring is included Martin Fowler's book <a href="{{ page.source_url }}">"Refactoring", 2nd Edition</a></li>
{% endif %}
{% if page.source != 'Martin Fowler' %}
{% if page.source == 'Joshua Kerievsky' %}
<li>This refactoring is included Joshua Kerievsky's book <a href="{{ page.source_url }}">"Refactoring to Patterns"</a></li>
{% endif %}
{% if page.source == 'Emily Bache' %}
<li>This refactoring was contributed to this site by {{ page.source }}</li>
{% endif %}

</ul>
</p>
<p>
Back to <a href="/reference/refactorings/index.html">All Refactorings</a>
</p>
</p>
2 changes: 1 addition & 1 deletion _learning_hours/legacy/extract_override.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ author: emilybache

# Extract and Override

"Extract and Override" is a classic dependency breaking technique from Michael Feathers' book "Working Effectively with Legacy Code". In this session we demonstrate it using the classic coding problem "TripService" by Sandro Mancuso.
This is a classic dependency breaking technique from Michael Feathers' book "Working Effectively with Legacy Code". In this session we learn how to use it on the classic coding problem "TripService" by Sandro Mancuso. Emily Bache has made a video ["Breaking Legacy Code Dependencies with Extract and Override"](https://www.youtube.com/watch?v=hwr9bVyXkTY) that explains more and shows a demo using Java.

## Learning Objectives
- Describe when to use a dependency breaking technique
Expand Down
3 changes: 2 additions & 1 deletion _learning_hours/refactoring/conditional_to_polymorphism.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ difficulty: 3
author: emilybache
affiliation: Praqma
tags: refactoring
name: replace_conditional_with_polymorphism
---


# Replace Conditional With Polymorphism

This refactoring replaces conditional logic with classes and polymorphism. The code smell that leads you to it is having a lot of similar switch statements that switch on type. In this session we'll practice doing it, and hopefully see just how good modern IDE tooling is for this kind of task.
Expand Down Expand Up @@ -70,6 +70,7 @@ or (CLion 2022.2.3, C++)
- Change Signature
- Introduce Constant...

You could also use the refactoring steps outlined in ["Replace Conditional with Polymorphism"]({% link _refactorings/replace_conditional_polymorphism.md %})

### Do Parrot
People should work in pairs to do the same exercise again from the start. If they lack IDE experience, they should follow your list of shortcuts from the whiteboard. More confident pairs may know other shortcuts and can try to achieve the same thing in a different way. Ask them to write down their sequence of steps if so.
Expand Down
48 changes: 48 additions & 0 deletions _learning_hours/refactoring/decompose_conditional.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
theme: refactoring
title: Decompose Conditional
kata: diagram_printer
difficulty: 3
author: emilybache
tags: refactoring
name: decompose_conditional
---

# Decompose Conditional
This is one of the refactorings from Martin Fowler's book.

## Learning Goals

* Describe the refactoring "Decompose Conditional"
* Use refactoring tools to decompose a conditional

## Session Outline

* 5 min connect: What is clean & tidy code?
* 5 min concept: Decompose Conditional
* 10 mins demo: TennisGame6, using IDE shortcuts
* 25 min do: Diagram Printer in pairs
* 5 min conclusions: Explain the main idea

### Connect - What is clean & tidy code?
[Pick the correct items]({% link _activities/connect/pick_the_correct_items_on_the_list.md %}) from a list like this:

* Write a comment explaining every line of code
* Instead of duplicating code, extract a method
* Leave unused variables in code comments for future reference
* Use short variable names like a,b,c
* ... your own ideas here

The idea is to get them thinking about what makes code good and readable.

### Concept - Decompose Conditional
This is one of the refactorings from Martin Fowler's book, explain what it is and why you might want to do it.

### Demo - TennisGame6
TennisGame6 from [Tennis Refactoring Kata](https://github.com/emilybache/Tennis-Refactoring-Kata) is designed to have a conditional that is easy to decompose. With your refactoring tools you should be able to do it quite quickly and show a version that is much easier to read and understand.

### Exercise - Tennis & DiagramPrinter
Get them first to repeat what you did with TennisGame6. If they are already quite good with their tools then they will also have time to work on [DiagramPrinter](https://github.com/emilybache/DiagramPrinter-Refactoring-Kata.git) - use the 'with_tests' branch. Ask them to work on decomposing and improving conditional logic in DiagramPrinter.doPrint() and DiagramPhysicalPrinter.doPrint().

### Conclusions - explain the main idea
[Explain the main idea]({% link _activities/conclusions/explain_main_idea.md %}) of 'Decompose Conditional'.
2 changes: 1 addition & 1 deletion _learning_hours/refactoring/simplify_conditional.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Examples of behaviour-preserving transformations for conditionals. I started a [
* Split & join if statements
* Two statements in one if statement -> two if statements (specialization of 'Split Loop')
* redundant 'else' when followed by one more if
* Normalize Conditional
* Normalize Conditional (aka lift-up conditional)

## Demo: show the patterns and the shape of the refactoring

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
theme: functional_programming
title: Replace Loop with Higher-Order Functions
difficulty: 2
author: nicolerauch
tags: functional_programming streams higher_order_functions design
code_smell: loops
name: replace_loop_with_higher_order_functions
---

# Replace Loop with Higher-Order Functions

Having a look at other programming paradigms broadens the horizon, and maybe you can even find
something that's useful in your daily work as a programmer, even while using a different paradigm.

Higher-order functions are at the core of functional programming, and other programming languages
also introduced this feature, where it may be called "lambda", "functional interface", "anonymous function" or
the like. Therefore it is worthwhile to find out more about them.


## Learning Objectives

* Learn to describe the three common higher-order functions filter, map, and reduce
* Learn how to apply them by converting a given piece of code that contains a loop.

## Session Outline

* 5 min connect: Higher-order functions
* 10 min concept: Common higher-order functions in various programming languages
* 30 min do: Apply common higher-order functions
* 10 min reflect: Typical use cases

### Connect

Ask these questions in the group, and give them some time to write down some answers on sticky notes etc.

* What do you call a function that takes a function as an argument or that returns a function?
* Have you ever done this in your code? What was the use-case?


The kinds of answers you are looking for are:
1) These functions are called higher-order functions.

2) - various applications related to the Java streaming API.
- callback functions
- event listeners
- decorators
- middleware pipelines
- function composition (i.e. building more complex functions from simple ones)
- strategy pattern (passing a specific algorithm to a function that implements the general structure)


### Concept: Common Higher-Order Functions

Common higher-order functions are:

1. filter
2. map
3. fold / reduce

That is their name in many programming languages.

C# LinQ calls them:

1. Where
2. Select
3. Aggregate

while Ruby and Smalltalk call them:

1. select
2. collect
3. inject

Explain how these work. Maybe draw some images to illustrate the flow of data.

* filter / Where / select:

Array -> Function -> Array

Takes an array and a boolean function, returns a new array that contains only those entries where the function returns true.

* map / Select / collect:

Array -> Function -> Array

Takes an array and a function, returns a new array that contains the results of the function applied to the values of the original array in the same order.

* fold or reduce / Aggregate / inject:

Array<V> -> (V,A)->A -> A -> A

Takes an array of values, an aggregator function that aggregates the array's values one by one into an output value,
and an initial output value, and returns the final output value (after all array values have been aggregated).


### Concrete: Applying Common Higher-Order Functions instead of Using Loops

Give the participants some loops that they can refactor to use filter, map, and reduce.

There are some examples with tests in ...repo... You can pick some or all of the provided examples.
Make sure to set a time box for each of the functions (~5 min for filter, ~5 min for map,
~10 min for reduce, with some grace time) so that everybody can work at their own pace.
Not everybody will be able to finish all of the exercises, which is totally fine.


### Conclusions: What are typical use cases for these functions?

Ask the following question in the group. First let them reflect silently for a minute or two, then discuss.

* Now that you have learned about the three common Higher-Order Functions filter, map, and reduce:
Do you have typical use cases in your production code where you could apply one of the three functions? Which are they?

<link to template>
42 changes: 42 additions & 0 deletions _refactorings/conditional_to_strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
layout: refactoring
title: Replace Conditional Logic with Strategy
source: Joshua Kerievsky
source_url: https://www.industriallogic.com/refactoring-to-patterns/catalog/conditionalWithStrategy.html
code_smells: deep_nesting
learning_hours:
---

# Replace Conditional Logic with Strategy

This is one of the refactorings in Joshua Kerievsky's book. This recipe is for the initial steps before the refactoring "Conditional to Polymorphism."

## Examine
Usually the prompt for this refactoring is unnecessary conditional complexity with deep nesting. In particular, when the program is changing behaviour based on a type code or a particular fixed set of conditions. If the class currently containing this behaviour is plausibly going to become the superclass in the Strategy pattern, then you could just extract a method and then go directly to "Conditional to Polymorphism."

## Prepare
* Highlight the block of code you intend to replace with the strategy pattern. It should contain all the conditional logic you want to replace. If the logic is contained in a loop, leave that outside.
* Think of a good class name for the strategy class, note it in a comment 'xxxStrategy'

## Implement
* 'Extract function' for the code you identified.
* If the new function is not static or top-level, there are additional steps:
* 'Introduce Parameter' for any data members it uses
* If necessary, introduce a parameter for an instance of the enclosing class so it can access methods on that class.
* Make the new method static or top-level
* Examine the parameter list to the new function and identify those that are specific to one strategy. [Introduce Parameter Object]({% link _refactorings/introduce_parameter_object.md %} for those parameters, name the new class as you identified earlier - 'xxxStrategy'. If there are no such arguments, just create that class.
* Make sure the new class is declared as a local variable before you call the new function, and is a parameter to it. Use 'Introduce Variable' or 'Change Signature' if needed.
* 'Move' the static or top level function to the new class as a static method or top-level function in the same file.
* For the new function, 'Make non-static' on the new class. If your tool doesn't have that refactoring then these are the steps:
* Manually put the function inside the class or remove the 'static' modifier.
* One of the arguments will be the xxxStrategy class that you added earlier. Assign this variable to the enclosing instance `var wrapper = this`. Remove the parameter.
* Inline the `wrapper` variable
* Update the call sites so `foo(wrapper)` becomes `wrapper.foo()`

## Clear
Check the names of the new method and class make sense. Check all the parameters to the new method make sense. Some of them might want to become constructor arguments, or you might want to move additional methods to this class.

## Follow up
Use ['Replace Conditional with Polymorphism']({% link _refactorings/replace_conditional_polymorphism.md %}) to make the concrete strategy classes.


33 changes: 33 additions & 0 deletions _refactorings/introduce_parameter_object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
layout: refactoring
title: Introduce Parameter Object
source: Martin Fowler
source_url: https://refactoring.com/catalog/introduceParameterObject.html
code_smells: long_parameter_list
learning_hours:
---

# Introduce Parameter Object

This is provided by some tools - Resharper has 'Transform Parameters', IntelliJ has 'Introduce Parameter Object'. If you don't have this available, then this checklist will help you to do it with only Extract Method and Inline Method.

## Identify
In a long parameter list, some might usefully be grouped together into a new class.

## Prepare
Work out which parameters you want to group together into a new class and ideally think of a good name for it.

## Implement

* Before the call site to the method, pretend you already have the new class, declare a variable for it and call its constructor with the arguments you want to put in the new type, eg `var wrapper = ParameterWrapper(a, b, c)`.
* Use your tools to create that class `ParameterWrapper`.
* Store all the constructor arguments in public fields.
* change all the references to these variables to use the fields of your new class, eg `a` becomes `wrapper.a`
* 'Extract method' on all the code that uses `wrapper`. The method should have the signature you wanted, and call the original method.
* Inline the call to the original method.

## Clear
Check the name of the new method is the same as the original.

## Follow up
Often you want to move the method to the type you just created.
2 changes: 2 additions & 0 deletions _refactorings/lift_up_conditional.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ learning_hours: lift_up_conditional_intro

# Lift-up Conditional
This refactoring was named by Emily Bache. She first learned it from Llewellyn Falco.
It's also related to Fowler's Remove Flag Argument https://refactoring.com/catalog/removeFlagArgument.html,
last example from the book is the begining of a lift up conditional.

## Examine
Look for a complex conditional that has a condition that is duplicated in more than one place. This is the conditional you will 'lift up' to be only in one place at the top. Note - in this process another conditional might become duplicated instead, so it doesn't always help.
Expand Down
34 changes: 34 additions & 0 deletions _refactorings/replace_conditional_polymorphism.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
layout: refactoring
title: Replace Conditional with Polymorphism
source: Martin Fowler
source_url: https://refactoring.com/catalog/replaceConditionalWithPolymorphism.html
code_smells: repeated_switches
learning_hours: replace_conditional_with_polymorphism
---

# Replace Conditional with Polymorphism

This refactoring replaces conditional logic with classes and polymorphism. The code smell that leads you to it is having a lot of similar switch statements that switch on type.

## Examine
Identify all the places with a repeated switch statement, that switch on the same type and have similar cases.

## Prepare
All these switches should be in functions on the same class - the one which "has" the type that is being switched on. Extract methods as needed and move them to the class which will become the base of the polymorphic hierarchy.

If the constructor to the class is called in a lot of places it's probably worth first doing 'replace constructor with factory method'.

## Implement

* Create new subclasses for each branch of the conditional. They should be empty with only a call to the superclass constructor
* Replace calls to the constructor of the base type with calls to the relevant subclass constructor. This will probably mean creating a new switch statement.
* 'Push Down' the first method containing the switch/conditional
* Remove redundant code in the pushed down method.
* 'Push Down' any additional fields or methods
* Remove redundant code and make the superclass abstract

## Clear
Consider whether the superclass should be an interface or an abstract class.


2 changes: 1 addition & 1 deletion _refactorings/split_loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Split Loop
source: Emily Bache
source_url: https://refactoring.com/catalog/splitLoop.html
code_smells: loops,divergent_change,shotgun_surgery
learning_hours: split_loop
learning_hours: split_loop,replace_loop_with_higher_order_functions
---

# Split Loop
Expand Down
Loading