diff --git a/lectures/refactoring/slides.qmd b/lectures/refactoring/slides.qmd index ad68ae09561c5ee0cdd349cad6040c45b63158b6..410878ea802960ae3643766ae48d567f082e62f1 100644 --- a/lectures/refactoring/slides.qmd +++ b/lectures/refactoring/slides.qmd @@ -1,5 +1,188 @@ --- title: "Refactoring and legacy code" subtitle: "" -author: "Florian Ziemen" --- + +# Refactoring + +## Motivation + +Software development often deals with rethinking decisions. + +Ultimately causes reworking portions of the code. + +Code is not final, it needs to evolve + + +## Why does it evolve? + +Software grows when + +- Integrating new features +- Optimizing current functionalities +- Extending tests +- Comment your code?! + + +## Definition + +The day will come when you need to _rewritte/rework_ or _restructure_ your code. It that _refactoring_? + +. . . + +> _Disciplined_ technique for restructuring an existing body of code, altering its internal structure _without changing_ its external _behavior_. + + + +## Refactoring example {auto-animate=true} + +```python +def fibonacci(n): + if n < 1: + return 0 + a, b = 0, 1 + for i in range(n-1): + a, b = b, a + b + return b +``` + +## Refactoring examples {auto-animate=true} + +:::{.aside} +Rewrite function for a more performant approach withouth changing external behaviour +::: +```python +def fibonacci(n): + if n < 0: + return n + + def multiply_matrices(A, B): + return [[A[0][0] * B[0][0] + A[0][1] * B[1][0], A[0][0] * B[0][1] + A[0][1] * B[1][1]],[A[1][0] * B[0][0] + A[1][1] * B[1][0], A[1][0] * B[0][1] + A[1][1] * B[1][1]]] + + n-=1 + matrix = [[1, 1], [1, 0]] + result = [[1, 0], [0, 1]] + while n: + if n % 2 == 1: + result = multiply_matrices(result, matrix) + matrix = multiply_matrices(matrix, matrix) + n //= 2 + + return result[0][0] +``` + +## Refactoring examples {auto-animate=true} + +:::{.aside} +Providing matrix multiplication separatly +::: +```python +def multiply_matrices(A, B): + return [[A[0][0] * B[0][0] + A[0][1] * B[1][0], A[0][0] * B[0][1] + A[0][1] * B[1][1]],[A[1][0] * B[0][0] + A[1][1] * B[1][0], A[1][0] * B[0][1] + A[1][1] * B[1][1]]] +``` +```python +def fibonacci(n): + if n < 0: + return n + + n-=1 + matrix = [[1, 1], [1, 0]] + result = [[1, 0], [0, 1]] + while n: + if n % 2 == 1: + result = multiply_matrices(result, matrix) + matrix = multiply_matrices(matrix, matrix) + n //= 2 + + return result[0][0] +``` + +## Refactoring examples {auto-animate=true} + +:::{.aside} +Provided matrix multiplication and [exponentiation](https://en.wikipedia.org/wiki/Exponentiation_by_squaring#With_constant_auxiliary_memory) separatly +::: +```python +def multiply_matrices(A, B): + return [[A[0][0] * B[0][0] + A[0][1] * B[1][0], A[0][0] * B[0][1] + A[0][1] * B[1][1]],[A[1][0] * B[0][0] + A[1][1] * B[1][0], A[1][0] * B[0][1] + A[1][1] * B[1][1]]] + + def matrix_power(base, exp): + result = [[1, 0], [0, 1]] + while exp: + if exp % 2 == 1: + result = multiply_matrices(result, base) + base = multiply_matrices(base, base) + exp //= 2 + return result +``` +```python +def fibonacci(n): + if n < 0: + return n + + result_matrix = matrix_power([[1, 1], [1, 0]],n-1) + return result_matrix[0][0] +``` + + +## Refactoring examples {auto-animate=true} + +```python +class Fibonacci + def __call__(self,n): + if n < 1: + return 0 + a, b = 0, 1 + for i in range(n-1): + a, b = b, a + b + return b +``` + +## Refactoring examples {auto-animate=true} + +::: {.aside} +Encapsulate behaviour using different types (function vs class) +::: +::: {.columns} +::: {.column} +```python +def fibonacci(n): + if n < 1: + return 0 + a, b = 0, 1 + for i in range(n-1): + a, b = b, a + b + return b + +print("Result:",fibonacci(10)) +``` +``` +Result: 55 +``` +::: +::: {.column} + +```python +class Fibonacci: + def __call__(self,n): + if n < 1: + return 0 + a, b = 0, 1 + for i in range(n-1): + a, b = b, a + b + return b + +fibonacci = Fibonacci() +print("Result:",fibonacci(10)) +``` +``` +Result: 55 +``` +::: +::: + +## How do we know its refactoring? + +- Behaviour cannot change! +- (Unit) Tests should not break +