Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Aprendendo solid com exemplos

2,246 views

Published on

Minha apresentação do 15º encontro do GURU-SP. Nela falei sobre exmplos de SOLID e como evitar as violações.

Aprendendo solid com exemplos

  1. 1. Aprendendo SOLID com exemplos Vinicius Baggio Fuentes ( @vinibaggio )
  2. 2. Atenção! Essa palestra pode ferir seus sentimentos!
  3. 3. $ whoami
  4. 4. Rails infelizmente fezmuita gente esquecer das boas práticas. Inclusive eu.
  5. 5. Rails: MUITAS convenções,mas saiba quando você deve sair delas
  6. 6. Skinny Controller, Fat models, certo?
  7. 7. CUIDADO!Fat ≠ Obese
  8. 8. Crie mais classes,nao fique restrito a AR::B!
  9. 9. YMMV
  10. 10. SOLID• Single Responsibility Rule (SRP)• Open-Closed Principle (OCP)• Liskov Substitution Principle (LSP)• Interface Segregation Principle (ISP)• Dependency Inversion Principle (DIP)
  11. 11. SRP• Esse é óbvio.
  12. 12. class Post < ActiveRecord::Base belongs_to :author has_many :comments validates_presence_of :title, :body after_create :log_creation after_update :log_update after_destroy :log_deletion private def log_creation Log.create(:message => "Post #{title} was created.") end def log_update Log.create(:message => "Post #{title} was updated.") end def log_deletion Log.create(:message => "Post #{title} was deleted.") endend
  13. 13. class Post < ActiveRecord::Base belongs_to :author has_many :comments Pode, Arnaldo? validates_presence_of :title, :body after_create :log_creation after_update :log_update after_destroy :log_deletion private def log_creation Log.create(:message => "Post #{title} was created.") end def log_update Log.create(:message => "Post #{title} was updated.") end def log_deletion Log.create(:message => "Post #{title} was deleted.") endend
  14. 14. Violação do SRP• Callbacks são conveniente, mas são prejudiciais• Chato de testar• Comportamento que não tem nada relacionado com Post• Comportamento inesperado (violação do Least Surprise Principle)
  15. 15. class Post < ActiveRecord::Base belongs_to :author has_many :comments validates_presence_of :title, :bodyend
  16. 16. class AuditedPost def self.create(post_attributes) post = Post.create(post_attributes) Log.log_creation(post) post end def self.update(post, post_attributes) post.update_attributes(post_attributes) Log.log_update(post) post end def self.destroy(post) post.destroy Log.log_destruction(post) post endend
  17. 17. OCP• Open for extension, closed for modification
  18. 18. Que porra é essa?
  19. 19. class User < ActiveRecord::Base # ... def to_json(options=nil) # ... end def to_xml # ... end def to_atom # ... endendclass UsersController < ApplicationController def show @user = User.find(params[:id]) respond_to do |format| format.html { render @user } format.json { render :text => @user.to_json } format.atom { render :text => @user.to_atom } end endend
  20. 20. class User < ActiveRecord::Base # ... def to_json(options=nil) # ... end def to_xml # ... end def to_atom # ... endendclass UsersController < ApplicationController def show @user = User.find(params[:id]) respond_to do |format| format.html { render @user } format.json { render :text => @user.to_json } format.atom { render :text => @user.to_atom } end endend
  21. 21. Para adicionar um novo formato, eu preciso alterar o controller
  22. 22. class UsersController < ApplicationController def show @user = User.find(params[:id]) Horrível! respond_to do |format| format.html { render @user } format.json { render :text => @user.to_json } format.atom { render :text => @user.to_atom } format.jsonp { render :text => "#{params[:callback]}(#{@user.to_json});" } end endendclass ArticlesController < ApplicationController def show @article = Article.find(params[:id]) respond_to do |format| format.html { render @article } format.json { render :text => @article.to_json } format.atom { render :text => @article.to_atom } format.jsonp { render :text => "#{params[:callback]}(#{@article.to_json});" } end endend
  23. 23. Nesse caso, a API deResponders do Rails 3 funciona bem!
  24. 24. class ApplicationController < ActionController::Base respond_to :html, :json, :atom, :jsonp # ...endclass UsersController < ApplicationController def show @user = User.find(params[:id]) respond_with(@user) endendclass ArticlesController < ApplicationController def show @article = Article.find(params[:id]) respond_with(@article) endend
  25. 25. Detectando cheiro de violação de OCP• #is_a? / #is_kind_of?• Reabrindo classes
  26. 26. LSP• Especializações de alguma interface não devem mudar produtos de métodos pai• Fácil de acontecer com Ruby, não precisamos respeitar assinaturas de método!
  27. 27. TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
  28. 28. irb :001 > 5.respond_to? :dup => true TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2 irb :003 >
  29. 29. irb :001 > 5.respond_to? :dup => trueirb :002 > 5.dup TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2 irb :003 >
  30. 30. TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
  31. 31. WTF?TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
  32. 32. WTF? WTF?TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
  33. 33. WTF? WTF?WTF? TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2 irb :003 >
  34. 34. WTF? WTF?WTF? TypeError: cant dup Fixnum from (irb):2:in `dup WTF? from (irb):2 irb :003 >
  35. 35. WTF?WTF? WTF?WTF? TypeError: cant dup Fixnum from (irb):2:in `dup WTF? from (irb):2 irb :003 >
  36. 36. WTF?WTF? WTF? WTF?WTF? TypeError: cant dup Fixnum from (irb):2:in `dup WTF? from (irb):2 irb :003 >
  37. 37. WTF?WTF? WTF? WTF?WTF? TypeError: cant dup Fixnum from (irb):2:in `dup WTF? irb :003 > WTF? from (irb):2
  38. 38. WTF?WTF? WTF? WTF? WTF?WTF? TypeError: cant dup Fixnum from (irb):2:in `dup WTF? irb :003 > WTF? from (irb):2
  39. 39. WTF?WTF? WTF? WTF? WTF?WTF? WTF? Fixnum TypeError: cant dup from (irb):2:in `dup WTF? irb :003 > WTF? from (irb):2
  40. 40. WTF?WTF? WTF? irb :001 > 5.respond_to? :dup => true WTF? WTF?WTF? WTF? Fixnum TypeError: cant dup from (irb):2:in `dup WTF? irb :003 > WTF? from (irb):2
  41. 41. WTF?WTF? WTF? irb :001 > 5.respond_to? :dup => true WTF? WTF? irb :002 > 5.dupWTF? WTF? Fixnum TypeError: cant dup from (irb):2:in `dup WTF? irb :003 > WTF? from (irb):2
  42. 42. ISP• Difícil de ver e exemplificar no Ruby por ser tipagem dinâmica• Restringir/reduzir uma interface grande para um ‘cliente’
  43. 43. ISP [2]• Rack Apps são ótimos exemplos disso!• Para interface Rack funcionar, basta uma array com três itens: • Código da resposta, headers e corpo.
  44. 44. ISP [3]• Ou seja, apps complexa de Rails, no final, retorna a mesma interface que a app a seguir
  45. 45. lambda { [200, {"Content-Type" => "text/plain"}, "Hello World!"]}
  46. 46. DIP• Fácil de observar e implementar
  47. 47. module Outpost module Notifiers class Campfire def notify(outpost, campfire_adapter=Outpost::Campfire::TinderAdapter) campfire = campfire_adapter.new @subdomain, :token => @token room = campfire.find_room_by_name @room status = outpost.last_status room.speak "System is #{status}: #{outpost.messages.join(,)}" end end endend
  48. 48. module Outpost module Notifiers class Campfire def notify(outpost, campfire_adapter=Outpost::Campfire::TinderAdapter) campfire = campfire_adapter.new @subdomain, :token => @token room = campfire.find_room_by_name @room status = outpost.last_status room.speak "System is #{status}: #{outpost.messages.join(,)}" end end endend
  49. 49. DIP - Se quisermos usar outra lib, basta usar outro adaptadormodule Outpost module Notifiers class Campfire def notify(outpost, campfire_adapter=Outpost::Campfire::TinderAdapter) campfire = campfire_adapter.new @subdomain, :token => @token room = campfire.find_room_by_name @room status = outpost.last_status room.speak "System is #{status}: #{outpost.messages.join(,)}" end end endend
  50. 50. • SOLID é difícil e exige prática• YMMV• As violações normalmente não vêm isoladas!• Leia LIVROS! Muita informação sobre o assunto• Processo contínuo de aprendizado
  51. 51. That’s all, folks! Perguntas?

×