Upcoming SlideShare
×

# 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
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Muito bom! Na próxima tenho que ir.

Are you sure you want to  Yes  No
Views
Total views
1,922
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
0
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?
8. 8. Crie mais classes,nao ﬁque 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 modiﬁcation
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 exempliﬁcar 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 ﬁnal, 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?