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

GURU SP - Design de aplicações orientadas a objeto

Elaine Naomi
October 27, 2018
80

GURU SP - Design de aplicações orientadas a objeto

Talk apresentada no Guru dia 27/10/2018

Elaine Naomi

October 27, 2018
Tweet

Transcript

  1. design de aplicações orientadas a objeto uma visão rubista

  2. hello

  3. DEPRECATED PHOTO Elaine Naomi Watanabe Desenvolvedora de Software (Plataformatec) Mestre

    em Ciência da Computação (USP) github.com/elainenaomi twitter.com/elaine_nw
  4. http://careers.plataformatec.com.br

  5. quais são os conceitos básicos de orientação a objeto como

    identificar problemas na nossa base de código como melhorar o design do nosso código o que vamos ver?
  6. orientação a objeto uma versão super resumida

  7. Objeto Classe

  8. Herança Polimorfismo Composição

  9. objetos + mensagens = app

  10. no mundo do Ruby...

  11. $ gem install rails

  12. $ gem install rails + Rails way

  13. View Model Controller apresentação intermediador dados + lógica de negócio

  14. None
  15. e se o projeto começa a crescer?

  16. None
  17. None
  18. + Funcionalidades

  19. + Funcionalidades + Modificações

  20. + Funcionalidades + Modificações + Bugs

  21. None
  22. Tamanho da base de código Tempo para deploy

  23. None
  24. como reconhecer os sintomas?

  25. None
  26. Muitas regras de negócio nos controllers Métodos muito longos Classes

    muito grandes
  27. Excesso de callbacks (controllers/models) Dificuldade para escrever testes Classes de

    difícil manutenção
  28. Ctrl + F / ⌘ + F para qualquer alteração

    Arquivos constantemente alterados
  29. e agora, como faz?

  30. DRY don't repeat yourself

  31. Helpers Presenters Single Table Inheritance Polymorphic Association

  32. Concerns (cuidado!)

  33. Service Objects

  34. Uso de Plain Old Ruby Object (PORO) Casos de uso

    Regras de negócio explícitas
  35. class ReserveBookService attr_reader :user, :book, :notification_service def initialize(user, book, notification_service)

    @user = user @book = book @notification_service = notification_service end def confirm! user.books << book notification_service.reservation_completed(user, book) end end
  36. Mais padrões Value Objects Form Objects Query Objects View Objects

    Policy Objects http://bit.ly/object_patterns
  37. SOLID

  38. None
  39. Single Responsibility Principle

  40. O que a minha classe faz? Indicativo de problemas: usar

    "e" ou "ou" na explicação
  41. Métodos relacionados? Uma única razão para mudar? Alterações com efeitos

    previsíveis?
  42. class BooksUser < ApplicationRecord belongs_to: :book belongs_to: :user after_commit :send_notification_reservation_completed

    def send_notification_reservation_completed NotificationService.reservation_completed(user, book) end end
  43. class BooksUser < ApplicationRecord belongs_to: :book belongs_to: :user after_commit :send_notification_reservation_completed

    def send_notification_reservation_completed NotificationService.reservation_completed(user, book) end end
  44. Uma classe de persistência não deveria saber sobre notificações a

    um usuário, por ex. Que tal um Service Object para isso?
  45. class ReserveBookService attr_reader :user, :book, :notification_service def initialize(user, book, notification_service)

    @user = user @book = book @notification_service = notification_service end def confirm! user.books << book notification_service.reservation_completed(user, book) end end
  46. Open/Closed Principle

  47. Adicionar nova regra = modificar uma ou mais classes? Se

    sim, é um indicativo de problema
  48. Aberto para extensão, fechado para modificação Defina interfaces/super classes Reduza

    o acoplamento
  49. class FinancialReport def generate(account, file_format) case file_format when :csv file

    = FormatCSV.generate_file(account.transactions) when :xml file = XML.parse_list(account.transactions) end Mailer.send(account.email, file) end end
  50. class FinancialReport def generate(account, file_format) case file_format when :csv file

    = FormatCSV.generate_file(account.transactions) when :xml file = XML.parse_list(account.transactions) when :pdf file = PDFGenerator.create(account.transactions) end Mailer.send(account.email, file) end end edição
  51. class FinancialReport def generate(account, file_format) case file_format when :csv file

    = FormatCSV.generate_file(account.transactions) when :xml file = XML.parse_list(account.transactions) when :pdf file = PDFGenerator.create(account.transactions) end Mailer.send(account.email, file) end end
  52. class FinancialReport def generate(account, file_creator) file = file_creator.create(account.transactions) Mailer.send(account.email, file)

    end end
  53. class FileCreator def create(items) raise NotImplementedError end end contrato

  54. class FileCreatorXML < FileCreator def create(items) XML.parse(items) end end class

    FileCreatorCSV < FileCreator def create(items) FormatCSV.generate_file(items) end end
  55. class FileCreatorPDF < FileCreator def create(items) PDFGenerator.generate(items) end end adição

  56. class FinancialReport def generate(account, file_creator) file = file_creator.create(account.transactions) Mailer.send(account.email, file)

    end end FinancialReport.new.generate(account, FileCreatorPDF.new)
  57. Liskov Substitution Principle

  58. Barbara Liskov Institute Professor from MIT The 2008 Turing Award

    winner liskov at csail.mit.edu
  59. Liskov Substitution Principle (1987) Let φ(x) be a property provable

    about objects x of type T. Then φ(y) should be true for objects y of type S where S is a subtype of T.
  60. Eita!

  61. Design by contract respeitar os contratos definidos pela classe base

  62. Pré-condições: dados de entrada classes derivadas só podem ser mais

    permissivas Pós-condições: dados de saída classes derivadas só podem ser mais restritivas Não podemos criar comportamentos inesperados ou incorretos! O comportamento da super classe precisa ser mantido
  63. class CheckingAccount # ... def deposit(value) raise InvalidValueError if value

    <= 0 self.balance = self.balance + value end def compute_bonus self.balance = self.balance * 1.01 end end
  64. class PayrollAccount < CheckingAccount class OperationNotAllowed < StandardError; end #

    ... def compute_bonus raise OperationNotAllowed end end
  65. CheckingAccount.all.each do |account| account.compute_bonus end

  66. CheckingAccount.all.each do |account| begin account.compute_bonus rescue PayrollAccount::OperationNotAllowed false end end

  67. CheckingAccount.all.each do |account| begin account.compute_bonus rescue PayrollAccount::OperationNotAllowed false end end

    contrato quebrado
  68. class PayrollAccount < CheckingAccount # ... def deposit(value) raise InvalidValueError

    if value <= 100 self.balance = self.balance + value end def compute_bonus self.balance = self.balance * 1.01 end end
  69. class PayrollAccount < CheckingAccount # ... def deposit(value) raise InvalidValueError

    if value <= 100 self.balance = self.balance + value end def compute_bonus self.balance = self.balance * 1.01 end end contrato quebrado
  70. Deveriam ser classes diferentes

  71. Exemplo clássico: Retângulo x Quadrado

  72. class Rectangle attr_reader :width, :height def initialize(width, height) @width =

    width @height = height end def area width * height end end
  73. class Square < Rectangle attr_reader :width, :height def initialize(width) @width

    = width @height = width end end contrato quebrado
  74. Interface Segregation Principle

  75. Uma classe derivada não deveria ser obrigada a implementar métodos

    que ela não usa
  76. class CoffeeMachine def brew_coffee # brew coffee logic end def

    fill_coffee_beans # fill coffee beans end end
  77. class Person attr_reader :coffee_machine def initialize @coffee_machine = CoffeeMachine.new End

    def quero_cafe coffee_machine.brew_coffee end end
  78. class Staff attr_reader :coffee_machine def initialize @coffee_machine = CoffeeMachine.new end

    def fill_coffee_beans coffee_machine.fill_coffee_beans end end
  79. Várias interfaces específicas é melhor do que uma interface generalizada

  80. class CoffeeMachineUserInterface def brew_coffee # brew coffee logic end end

    class CoffeeMachineServiceInterface def fill_coffee_beans # fill coffee beans end end
  81. None
  82. class CoffeeMachineUserInterface def brew_coffee # brew coffee logic end end

    class CoffeeMachineServiceInterface def fill_coffee_beans # fill coffee beans end end
  83. class Person attr_reader :coffee_machine def initialize @coffee_machine = CoffeeMachineUserInterface.new end

    def quero_cafe coffee_machine.brew_coffee end end `
  84. class Person attr_reader :coffee_machine def initialize @coffee_machine = CoffeeMachineUserInterface.new end

    def quero_cafe coffee_machine.brew_coffee end end ` `
  85. class Staff attr_reader :coffee_machine def initialize @coffee_machine = CoffeeMachineServiceInterface.new end

    def fill_coffee_beans coffee_machine.fill_coffee_beans end end
  86. class Staff attr_reader :coffee_machine def initialize @coffee_machine = CoffeeMachineServiceInterface.new end

    def fill_coffee_beans coffee_machine.fill_coffee_beans end end
  87. Dependency Inversion Principle

  88. Dependa de abstrações, não de implementações

  89. class FinancialReport def generate(account, file_format) case file_format when :csv file

    = FormatCSV.generate_file(account.transactions) when :xml file = XML.parse_list(account.transactions) when :pdf file = PDFGenerator.create(account.transactions) end Mailer.send(account.email, file) end end
  90. class FinancialReport def generate(account, file_creator) file = file_creator.create(account.transactions) Mailer.send(account.email, file)

    end end
  91. class FileCreatorCSV < FileCreator def create(items) FormatCSV.generate_file(items) end End FinancialReport.new.generate(account,

    FileCreatorCSV.new)
  92. class FileCreatorCSV < FileCreator def create(items) FormatCSV.generate_file(items) NewCSVGenerator.parse(items, header: false)

    end End FinancialReport.new.generate(account, FileCreatorCSV.new)
  93. TL;DR;

  94. TL;DR: Alta coesão Baixo acoplamento Encapsulamento

  95. None
  96. vamos refatorar tudo?

  97. None
  98. esses conceitos nos ajudam a criar aplicações mais flexíveis

  99. converse com seu time

  100. analisem juntos os trade-offs

  101. cuidado com big design up front

  102. e não esqueçam:

  103. codar é um processo de comunicação

  104. None
  105. Mais sobre design? Padrões de projeto TDD, DDD

  106. minhas referências

  107. None
  108. None
  109. None
  110. Até a próxima!

  111. obrigada speakerdeck.com/elainenaomi