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

Design de aplicações orientadas a objeto

Design de aplicações orientadas a objeto

Modelamos aplicações Ruby como um conjunto de objetos interagindo entre si para resolver um problema.

Quando propomos uma solução, ela geralmente é validada dentro de um escopo específico.

Mas mudanças sempre acontecem: no código, nas regras de negócio, na vida, no universo e em tudo mais.

Com essas mudanças, será que uma solução inicialmente proposta continua sendo válida? Se não for, o código implementado está preparado para evoluir e agregar novos comportamentos de maneira saudável?

Nesta talk, vamos analisar princípios e padrões de design de aplicações orientadas a objeto e como podemos aplicá-los no dia a dia a fim de tornar nosso código mais flexível e com maior qualidade.

Elaine Naomi

July 21, 2018
Tweet

More Decks by Elaine Naomi

Other Decks in Programming

Transcript

  1. Elaine Naomi Watanabe Desenvolvedora de Software (Plataformatec) Mestre em Ciência

    da Computação (USP) github.com/elainenaomi twitter.com/elaine_nw
  2. 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?
  3. Objeto abstração de uma parte do domínio/problema estados/valores específicos para

    seus atributos Classe template/gabarito para definição de objetos um conjunto de atributos (características) e métodos (comportamentos)
  4. Herança herdar atributos/métodos comuns de uma classe base (ou superclasse)

    e adicionar novos atributos/métodos Polimorfismo ter uma interface única de acesso para diferentes classes/objetos Composição combinar objetos simples de modo a ter objetos mais complexos
  5. Ctrl + F / ⌘ + F para qualquer alteração

    Arquivos constantemente alterados
  6. Uso de Plain Old Ruby Object (PORO) Casos de uso

    Regras de negócio explícitas
  7. 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
  8. 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
  9. 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
  10. 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?
  11. 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
  12. 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
  13. 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
  14. 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
  15. class FileCreatorXML < FileCreator def create(items) XML.parse(items) end end class

    FileCreatorCSV < FileCreator def create(items) FormatCSV.generate_file(items) end end
  16. 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
  17. 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
  18. class PayrollAccount < CheckingAccount class OperationNotAllowed < StandardError; end #

    ... def compute_bonus raise OperationNotAllowed end end
  19. 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
  20. 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
  21. class CoffeeMachine def brew_coffee # brew coffee logic end def

    fill_coffee_beans # fill coffee beans end end
  22. class Staff attr_reader :coffee_machine def initialize @coffee_machine = CoffeeMachine.new end

    def fill_coffee_beans coffee_machine.fill_coffee_beans end end
  23. class CoffeeMachineUserInterface def brew_coffee # brew coffee logic end end

    class CoffeeMachineServiceInterface def fill_coffee_beans # fill coffee beans end end
  24. class CoffeeMachineUserInterface def brew_coffee # brew coffee logic end end

    class CoffeeMachineServiceInterface def fill_coffee_beans # fill coffee beans end end
  25. Não exponha a regra de negócio Não deixe quem usa

    o objeto tomar decisões por ele com base no seu estado Confie no seu objeto!