Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Easy and complex

Easy and complex

Sobolev Nikita

February 08, 2020
Tweet

More Decks by Sobolev Nikita

Other Decks in Programming

Transcript

  1. 4

  2. 6

  3. "Я не знаю, что делает функция на 500 строк, но

    могу сразу сказать, что она слишком сложная" – Неизвестный автор 8
  4. M = E − N + 2P M = цикломатическая

    сложность, E = количество рёбер в графе, N = количество узлов в графе, P = количество компонент связности. 12
  5. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { for (int j = 2; j < i; ++j) { if (i % j == 0) { continue OUT; } } total += i; } return total; } 14
  6. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { if (i % j == 0) { continue OUT; } } total += i; } return total; } 15
  7. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { // +1 if (i % j == 0) { continue OUT; } } total += i; } return total; } 16
  8. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { // +1 if (i % j == 0) { // +1 continue OUT; } } total += i; } return total; } // Cyclomatic Complexity 4 17
  9. String getWords(int number) { // +1 switch (number) { case

    1: // +1 return "one"; case 2: // +1 return "a couple"; case 3: // +1 return "several"; default: return "lots"; } } // Cyclomatic Complexity 4 21
  10. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { // +1 if (i % j == 0) { // +1 continue OUT; } } total += i; } return total; } // Cyclomatic Complexity 4 22
  11. 25

  12. > Increment when there is a break in the linear

    (top-to-bottom, left- to-right) flow of the code 25
  13. > Increment when there is a break in the linear

    (top-to-bottom, left- to-right) flow of the code > Increment when structures that break the flow are nested 25
  14. > Increment when there is a break in the linear

    (top-to-bottom, left- to-right) flow of the code > Increment when structures that break the flow are nested > Ignore "shorthand" structures that readably condense multiple lines of code into one 25
  15. // Cyclomatic Cognitive String getWords(int number) { // +1 switch

    (number) { // +1 case 1: // +1 return "one"; case 2: // +1 return "a couple"; default: // +1 return "lots"; } } // =4 =1 26
  16. // Cyc Cog int sumOfPrimes(int max) { // +1 int

    total = 0; OUT: for (int i = 1; i <= max; ++i) { // +1 +1 for (int j = 2; j < i; ++j) { // +1 +2 (nesting=1) if (i % j == 0) { // +1 +3 (nesting=2) continue OUT; // +1 } } total += i; } return total; } // =4 =7 27
  17. String getWords(int number) { switch (number) { case 1: return

    "one"; case 2: return "a couple"; case 3: return "several"; default: return "lots"; } } Просто 28
  18. int sumOfPrimes(int max) { int total = 0; OUT: for

    (int i = 1; i <= max; ++i) { for (int j = 2; j < i; ++j) { if (i % j == 0) { continue OUT; } } total += i; } return total; } Сложно 29
  19. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 36
  20. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 37
  21. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 38
  22. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 39
  23. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 40
  24. <_ast.Module> ┗━ body ┗━ [1] <_ast.Expr> ┗━ value: <_ast.Call> ┣━

    args ┃ ┣━ [0] <_ast.BinOp> ┃ ┃ ┣━ left: <_ast.BinOp> ┃ ┃ ┃ ┣━ left: <_ast.Name> ┃ ┃ ┃ ┃ ┗━ id: first ┃ ┃ ┃ ┣━ op: <_ast.Mult> ┃ ┃ ┃ ┗━ right: <_ast.Constant> ┃ ┃ ┃ ┗━ value: 5 ┃ ┃ ┣━ op: <_ast.Add> ┃ ┃ ┗━ right: <_ast.BinOp> ┃ ┃ ┣━ left: <_ast.Attribute> ┃ ┃ ┃ ┣━ attr: pi ┃ ┃ ┃ ┗━ value: <_ast.Name> ┃ ┃ ┃ ┗━ id: math ┃ ┃ ┣━ op: <_ast.Mult> ┃ ┃ ┗━ right: <_ast.Constant> ┃ ┃ ┗━ value: 2 ┃ ┣━ [1] <_ast.Call> ┃ ┃ ┣━ args ┃ ┃ ┃ ┗━ [0] <_ast.Starred> ┃ ┃ ┃ ┗━ value: <_ast.Name> ┃ ┃ ┃ ┗━ id: matrix ┃ ┃ ┗━ func: <_ast.Attribute> ┃ ┃ ┗━ value: <_ast.Name> ┃ ┃ ┗━ id: matrix ┃ ┗━ [2] <_ast.Call> ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: matrix ┃ ┃ ┗━ [1] <_ast.Constant> ┃ ┃ ┗━ value: 2 ┃ ┗━ func: <_ast.Attribute> ┃ ┣━ attr: show ┃ ┗━ value: <_ast.Name> ┃ ┗━ id: display ┗━ func: <_ast.Name> ┗━ id: print 42
  25. Структуры > OverusedExpressionViolation > OverusedStringViolation > TooLongYieldTupleViolation > TooLongCompareViolation >

    TooLongTryBodyViolation > TooDeepAccessViolation > TooLongCallChainViolation > TooDeepNestingViolation > TooManyConditionsViolation > TooManyElifsViolation > TooManyForsInComprehensionViolation > TooManyExceptCasesViolation 48
  26. 53

  27. Процесс: > Пишем простые блоки кода > В какой-то момент

    сложность переполняется > Рефакторим 55
  28. -- the type of monad m data m a =

    ... -- return takes a value and embeds it in the monad. return :: a -> m a -- bind is a function that combines m a with a computation -- monad instance m b (>>=) :: m a -> (a -> m b) -> m b
  29. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 67
  30. Насколько сложна ваша предметная область? > Порог входа > Термины

    и их количество > Процессы > Правила > Варианты использования 74
  31. 76

  32. 78

  33. 79

  34. 80

  35. sum : (single : Bool) -> isSingleton single -> Nat

    sum True x = x sum False [] = 0 sum False (x :: xs) = x + sum False xs 83
  36. 84

  37. Callable[[List[int]], List[str]] • Выбросит ли она исключение? • Является ли

    она чистой? • Будет ли добиться в базу или http? 88
  38. Callable[[List[int]], List[str]] • Выбросит ли она исключение? Да? Result[List[int], Exception]

    • Является ли она нечистой? Да? IO[List[int]] • Все вместе? Конечно! IO[Result[List[int], Exception]] 89
  39. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 90
  40. def fetch_user_profile(user_id: int) -> IOResultE['User']: def _make_request( user_id: int, )

    -> IOResultE[requests.Response]: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response def _parse_json( response: requests.Response, ) -> ResultE['User']: return response.json() 91
  41. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 92
  42. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 93
  43. values := << 1, 2, 4, 8 >> -- Sum

    of (index * values [index]). across values as i from sum := 0 loop sum := sum + i.cursor_index * i.item end 96
  44. @deal.pre(lambda *args: all(arg > 0 for arg in args)) @deal.post(lambda

    result: result > 5) def sum_positive(*args): return sum(args) sum_positive(1, 2, 3, 4) # 10 sum_positive(1, 2, -3, 4) # PreContractError: sum_positive(1, 2) # PostContractError: 98
  45. @deal.inv(lambda post: post.likes >= 0) class Post: likes = 0

    post = Post() post.likes = 10 post.likes = -10 # InvContractError: 99
  46. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 105
  47. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 106
  48. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 107
  49. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 108
  50. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 109
  51. [importlinter:contract:violation-independence] name = Independence contract for violations type = independence

    modules = django_project.billing_app django_project.auth_app django_project.orders_app django_project.statistics_app 111
  52. [importlinter:contract:violation-independence] name = Independence contract for violations type = independence

    modules = django_project.billing_app django_project.auth_app django_project.orders_app django_project.statistics_app 112
  53. [importlinter:contract:violation-independence] name = Independence contract for violations type = independence

    modules = django_project.billing_app django_project.auth_app django_project.orders_app django_project.statistics_app 113
  54. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 115
  55. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 116
  56. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 117
  57. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 118
  58. Что делать то? > Использовать DDD > Строить чистую архитектуру

    > Следить за качеством абстракций > Документировать и создавать обучающие материалы 124
  59. 127

  60. 128

  61. 132

  62. 133

  63. 135

  64. Полезные ссылки > sobolevn.me/2019/10/complexity- waterfall > github.com/wemake-services/wemake- python-styleguide > wemake-python-stylegui.de/en/latest/

    pages/usage/violations/complexity.html > sobolevn.me/2019/02/python-exceptions- considered-an-antipattern 143
  65. Полезные ссылки > sobolevn.me/2019/10/complexity- waterfall > github.com/wemake-services/wemake- python-styleguide > wemake-python-stylegui.de/en/latest/

    pages/usage/violations/complexity.html > sobolevn.me/2019/02/python-exceptions- considered-an-antipattern > github.com/dry-python/returns 143