Aprendendo SOLID  com exemplos Vinicius Baggio Fuentes ( @vinibaggio )
Atenção! Essa palestra   pode ferir seus    sentimentos!
$ whoami
Rails infelizmente fezmuita gente esquecer das boas práticas.    Inclusive eu.
Rails: MUITAS     convenções,mas saiba quando você   deve sair delas
Skinny Controller, Fat models,           certo?
CUIDADO!Fat ≠ Obese
Crie mais classes,nao fique restrito a      AR::B!
YMMV
SOLID• Single Responsibility Rule (SRP)• Open-Closed Principle (OCP)• Liskov Substitution Principle (LSP)• Interface Segre...
SRP• Esse é óbvio.
class Post < ActiveRecord::Base  belongs_to :author has_many :comments validates_presence_of :title, :body after_create :l...
class Post < ActiveRecord::Base  belongs_to :author has_many :comments            Pode, Arnaldo? validates_presence_of :ti...
Violação do SRP• Callbacks são conveniente, mas são  prejudiciais• Chato de testar• Comportamento que não tem nada  relaci...
class Post < ActiveRecord::Base  belongs_to :author has_many :comments  validates_presence_of :title, :bodyend
class AuditedPost  def self.create(post_attributes)    post = Post.create(post_attributes)    Log.log_creation(post)    po...
OCP• Open for extension, closed for modification
Que porra é essa?
class User < ActiveRecord::Base  # ...  def to_json(options=nil)    # ...  end  def to_xml    # ...  end  def to_atom    #...
class User < ActiveRecord::Base  # ...  def to_json(options=nil)    # ...  end  def to_xml    # ...  end  def to_atom    #...
Para adicionar um novo  formato, eu preciso  alterar o controller
class UsersController < ApplicationController  def show    @user = User.find(params[:id])                                 ...
Nesse caso, a API deResponders do Rails 3   funciona bem!
class ApplicationController < ActionController::Base  respond_to :html, :json, :atom, :jsonp  # ...endclass UsersControlle...
Detectando cheiro de  violação de OCP• #is_a? / #is_kind_of?• Reabrindo classes
LSP• Especializações de alguma interface não  devem mudar produtos de métodos pai• Fácil de acontecer com Ruby, não  preci...
TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
irb :001 > 5.respond_to? :dup => true  TypeError: cant dup Fixnum   from (irb):2:in `dup   from (irb):2  irb :003 >
irb :001 > 5.respond_to? :dup => trueirb :002 > 5.dup  TypeError: cant dup Fixnum   from (irb):2:in `dup   from (irb):2  i...
TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
WTF?TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
WTF?                  WTF?TypeError: cant dup Fixnum from (irb):2:in `dup from (irb):2irb :003 >
WTF?                     WTF?WTF? TypeError:   cant dup Fixnum    from (irb):2:in `dup    from (irb):2   irb :003 >
WTF?                     WTF?WTF? TypeError:   cant dup Fixnum    from (irb):2:in `dup                WTF?    from (irb):2...
WTF?WTF?                 WTF?WTF? TypeError:   cant dup Fixnum    from (irb):2:in `dup                WTF?    from (irb):2...
WTF?WTF?               WTF?                  WTF?WTF? TypeError:   cant dup Fixnum    from (irb):2:in `dup                ...
WTF?WTF?               WTF?                  WTF?WTF? TypeError:   cant dup Fixnum    from (irb):2:in `dup  WTF?   irb :00...
WTF?WTF?         WTF?       WTF? WTF?WTF? TypeError:   cant dup Fixnum    from (irb):2:in `dup  WTF?   irb :003 >   WTF?  ...
WTF?WTF?               WTF?     WTF? WTF?WTF?        WTF? Fixnum TypeError: cant dup    from (irb):2:in `dup WTF?   irb :0...
WTF?WTF?               WTF? irb :001 > 5.respond_to? :dup  => true     WTF? WTF?WTF?        WTF? Fixnum TypeError: cant du...
WTF?WTF?                  WTF?  irb :001 > 5.respond_to? :dup   => true        WTF? WTF? irb :002 > 5.dupWTF?           WT...
ISP• Difícil de ver e exemplificar no Ruby por  ser tipagem dinâmica• Restringir/reduzir uma interface grande  para um ‘cli...
ISP [2]• Rack Apps são ótimos exemplos disso!• Para interface Rack funcionar, basta uma  array com três itens:  • Código d...
ISP [3]• Ou seja, apps complexa de Rails, no final,  retorna a mesma interface que a app a  seguir
lambda {    [200,    {"Content-Type" => "text/plain"},    "Hello World!"]}
DIP• Fácil de observar e implementar
module Outpost  module Notifiers    class Campfire      def notify(outpost, campfire_adapter=Outpost::Campfire::TinderAdap...
module Outpost  module Notifiers    class Campfire      def notify(outpost, campfire_adapter=Outpost::Campfire::TinderAdap...
DIP - Se quisermos usar outra lib,                              basta usar outro adaptadormodule Outpost  module Notifiers...
• SOLID é difícil e exige prática• YMMV• As violações normalmente não vêm  isoladas!• Leia LIVROS! Muita informação sobre ...
That’s all, folks!      Perguntas?
Aprendendo solid com exemplos
Upcoming SlideShare
Loading in …5
×

Aprendendo solid com exemplos

1,922 views
1,826 views

Published on

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

1 Comment
10 Likes
Statistics
Notes
No Downloads
Views
Total views
1,922
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
0
Comments
1
Likes
10
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • 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?

    ×