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?
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
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
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
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)
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.
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
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
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
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
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)