7. analisar 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?
25. class Pessoa
def initialize(nome_recebido, forma_de_contato_recebida)
@nome = nome_recebido
@contato_favorito = forma_de_contato_recebida
end
def contato
@contato_favorito.identificador
end
end exemplo de composição
48. Quando reescrever um sistema?
Uso de tecnologia desatualizada
Contratação de pessoas está difícil
Existência de tecnologias mais vantajosas
Fonte: http://blog.plataformatec.com.br/2016/07/key-points-to-consider-when-doing-a-software-rewrite/
59. Como medir a necessidade de refatoração?
Quantidade de código duplicado
Dificuldade para escrever testes
Desempenho da suíte de testes
Identificação dos code smells
60. Code smells
Sintomas no código que indicam possíveis
problemas de design em sistemas orientados a objeto
67. # a é o valor total a ser cobrado
def charge(a)
# cartão com status = 3 é o cartão ativo
if credit_card.status == 3
payment_gateway.charge(a)
end
end
68. def charge(total)
# cartão com status = 3 é o cartão ativo
if credit_card.status == 3
payment_gateway.charge(total)
end
end segundos de vida economizados! o/
69. def charge(total)
# cartão com status = 3 é o cartão ativo
if credit_card.status == 3
payment_gateway.charge(total)
end
end
70. def charge(total)
# cartão com status = 3 é o cartão ativo
if credit_card.status == 3
payment_gateway.charge(total)
end
end
71. def charge(total)
# cartão com status = 3 é o cartão ativo
if credit_card.active?
payment_gateway.charge(total)
end
end
75. # Public: Integer number of seconds to wait
# before connection timeout.
CONNECTION_TIMEOUT = 60
76. # Public: A summary of how much some user has consumed in a
certain plan.
#
# Examples
# plan_consumption_summary(contracted_plan)
# # => '2.44% (500 MB of 20 GB)'
#
# Returns a String.
def plan_consumption_summary(contracted_plan)
total_contracted = contracted_plan.plan_storage_limit
total_consumed = contracted_plan.total_consumed
# ...
Fonte: http://blog.plataformatec.com.br/2018/06/the-anatomy-of-code-documentation/
97. 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
98. 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
99. 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?
Mais exemplos em: codeclimate.com/blog/7-ways-to-decompose-fat-activerecord-models/
100. 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
101. 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
103. Adicionar nova regra = modificar uma ou mais classes?
Se sim, é um indicativo de problema
104. Aberto para extensão, fechado para modificação
Defina interfaces/super classes
Reduza o acoplamento
105. 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
106. 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
107. 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
110. class FileCreatorXML < FileCreator
def create(items)
XML.parse(items)
end
end
class FileCreatorCSV < FileCreator
def create(items)
FormatCSV.generate_file(items)
end
end
111. class FileCreatorPDF < FileCreator
def create(items)
PDFGenerator.generate(items)
end
end
adição
112. 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)
115. 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.
Tradução em: https://speakerdeck.com/elainenaomi/hacking-evening-liskov-substitution-principle
118. 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
119. 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
120. class PayrollAccount < CheckingAccount
class OperationNotAllowed < StandardError; end
# ...
def compute_bonus
raise OperationNotAllowed
end
end
124. 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
125. 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
137. class Staff
attr_reader :coffee_machine
def initialize
@coffee_machine = CoffeeMachineServiceInterface.new
end
def fill_coffee_beans
coffee_machine.fill_coffee_beans
end
end
138. class Staff
attr_reader :coffee_machine
def initialize
@coffee_machine = CoffeeMachineServiceInterface.new
end
def fill_coffee_beans
coffee_machine.fill_coffee_beans
end
end
142. class FileCreatorCSV < FileCreator
def create(items)
FormatCSV.generate_file(items)
end
End
FinancialReport.new.generate(account, FileCreatorCSV.new)
143. class FileCreatorCSV < FileCreator
def create(items)
FormatCSV.generate_file(items)
NewCSVGenerator.parse(items, header: false)
end
End
FinancialReport.new.generate(account, FileCreatorCSV.new)
167. Refactoring rails apps - Flavia Fortes
http://bit.ly/2zDADhe
Evitando o Jenga Driven Development - João Britto
http://bit.ly/2DFvB3v
Mais referências:
168. Por que (às vezes) você deve reinventar a roda -
Paulo Silva
https://youtu.be/kdNf2abcP5E?t=11640
Callbacks do ActiveRecord: o mal secreto ou
apenas mal compreendidos? - Rondy
https://youtu.be/kdNf2abcP5E?t=13214
Mais referências: