Refatorando Ruby
Técnicas de Orientação a Objetos e Design Patterns
         Aplicados a Linguagens Dinâmicas




        ...
cassiomarques.wordpress.com

     @cassiomarques

   cassiommc@gmail.com
http://www.flickr.com/photos/docman/




  Refatoração
Motivações
Muitos programadores
  usando Ruby não
  sabem usar OO
“Raciocínio estático”
Linguagem nova, mas
   hábitos antigos
Organização
Modularizar o código
Facilitar manutenção
Facilitar compreensão
do código para novos
  desenvolvedores
Design Patterns
Motivações
Ajudar o Ruby a ser
   “Enterprise”
Patterns implementados X Enterprisy
YAGNI
Um exemplo simples...
 1   class Bhaskara
 2     def solve(a, b, c)
 3       # Calcula o delta
 4       d = b ** 2 - 4 * a * c
 5
 6       if d ...
1 class Bhaskara  
2 end
1   class Bhaskara
2     def initialize(a, b, c)
3       @a, @b, @c = a, b, c
4     end
5   end
 1   class Bhaskara
 2     def initialize(a, b, c)
 3       @a, @b, @c = a, b, c
 4     end
 5
 6     private
 7     def d...
 1   class Bhaskara
 2     def initialize(a, b, c)
 3       @a, @b, @c = a, b, c
 4     end
 5
 6     def print_results
 7...
 1   class Bhaskara
 2     def initialize(a, b, c)
 3       @a, @b, @c = a, b, c
 4     end
 5
 6     def print_results
 7...
O que eu preciso para
     refatorar?
TESTES
Mantenha seus métodos
      pequenos




                http://www.flickr.com/photos/davidden/
 1   class ShoppingCart
 2     def total_value
 3       total = 0
 4       for item in @items
 5         sub_total = item....
 1   # - Elimina variáveis temporárias em favor de métodos
 2   class ShoppingCart
 3     def total_value
 4       total =...
 1   # - Método usa apenas atributos de outro objeto
 2   #  => Mover!
 3   class ShoppingCart
 4     def total_value
 5  ...
 1   # - Método fazendo coisa demais
 2   # => Extrair!
 3   class ShoppingCart
 4     def total_value
 5       total = 0
...
 1   # - Use Ruby!
 2   class ShoppingCart
 3     def total_value
 4       items.inject(0.0) { |sum, item| 
 5         sum...
Faça objetos se comportarem
       como coleções




                   http://www.flickr.com/photos/deepsignal/366774303/s...
 1   class Library
 2     def search_book_by_title(title)
 3       @books.select { |book| book.title == title }
 4     end...
 1   class Library
 2     include Enumerable
 3
 4     def each
 5       @books.each { |book| yield book }
 6     end
 7  ...
Dê nomes aos parâmetros
           dos métodos




                 http://www.flickr.com/photos/giantginkgo/
 1   class Dialog
 2     def initialize(height, width, name, title)
 3       @height = height
 4       @width  = width
 5 ...
 1   class Dialog
 2     def initialize(options)
 3       @height = options[:height]
 4       @width  = options[:width]
 5...
Anotações




http://www.flickr.com/photos/matski_98/
 1   class Person
 2     def state=(state)
 3       unless [:sick, :healthy].include?(state)
 4         raise ArgumentErro...
 1   module HasStates
 2     def has_states(*args)
 3       define_method :state= do |new_state|
 4         unless args.in...
 1   class Person
 2     extend HasStates
 3
 4     has_states :sick, :healthy
 5   end
 6
 7   class Vehicle
 8     exten...
Uma classe não deve
realizar trabalhos que
 não lhe pertencem...
1   class Person
2     attr_accessor :telephone_number, :telephone_area_code
3
4     def formatted_telephone
5       "(#{t...
 1   class Person
 2     def initialize
 3       @telephone_number = TelephoneNumber.new
 4     end
 5   end
 6
 7   class...
Delegue, delegue, delegue...
 1   class Queue
 2     def initialize
 3       @queue = []
 4     end
 5
 6     def enqueue(element)
 7       @queue.unsh...
O Ruby pode dar uma
     ajudinha...
 1   require 'forwardable'
 2
 3   class Queue
 4     extend Forwardable
 5
 6     def initialize
 7       @queue = []
 8 ...
Não exponha o interior de
seus objetos sem necessidade




                         http://www.flickr.com/photos/mwichary/
 1   class Room
 2     attr_reader :air_conditioner
 3   end
 4
 5   class AirConditioner
 6     attr_reader :sensor
 7   ...
 1   class Room
 2     attr_reader :air_conditioner
 3
 4     def temperature
 5       air_conditioner.sensor.temperature
...
Substitua números
mágicos por constantes




http://decluttr.com/4524127771_white
1   class Circle
2     def area
3       3.14159265 * (r ** 2)
4     end
5   end
1   class Circle
2     PI = 3.14159265 
3
4     def area
5       PI * (r ** 2)
6     end
7   end
Encapsule coleções de
                                                        objetos




http://www.flickr.com/photos/eagl...
 1   class Cart
 2     attr_accessor :products
 3   end
 4
 5   class Product
 6     attr_reader :value, :name
 7
 8     d...
1   products = []
2   products << Product.new('Camiseta', 35.00)
3   products << Product.new('Bermuda', 38.00)
4   product...
 1   class Cart
 2     def initialize
 3       @products = []
 4     end
 5
 6     def add_product(product)
 7       @prod...
Substitua condicionais por
             polimorfismo




                   http://www.flickr.com/photos/randyread/
 1   class Tax
 2     def initialize(type, value)
 3       @type = type    
 4       @value = value
 5     end
 6
 7     d...
 1   module Tax
 2     def due_value
 3       @due_rate * @value    
 4     end
 5
 6     def retained_value
 7       @ret...
 1   class Invoice
 2     def initialize(value)
 3       @value = value
 4       create_taxes
 5     end
 6
 7     def add...
Simplifique expressões
     condicionais
1 if product.release_date < 3.months.ago || quantity > 100
2   self.value *= 0.90
3 end
 1   if elegible_for_discount?
 2     self.value *= 0.90
 3   end
 4
 5   def elegible_for_discount?
 6     old? && great_...
http://www.flickr.com/photos/jeff_oliver/




                                                 Mas uma expressão
          ...
1   if age > 40
2     if gender == :male
3       if last_visit > 1.month.ago
4         # ...
5       end
6     end
7   end
1   if age > 40 && gender == :male && last_visit > 1.month
2     # ...
3   end
4
5   # e a partir daqui podemos extrair as...
Às vezes faz mais
sentido juntar do que
      separar...
1   class Bicycle
2     def press_front_brake
3       brakes[:front].press
4     end
5
6     def press_rear_brake
7       ...
1   class Bicycle
2     def press_brake(brake)
3       brakes[brake].press
4     end
5   end
Verifique se a mensagem
                                         pode ser enviada




http://www.flickr.com/photos/funtik/
1   def do_something(target)
2     target.prepare rescue nil
3     target.explode
4   end
1   def do_something(target)
2     target.prepare if target.respond_to? :prepare
3     target.explode
4   end
Herança
 1   class CdPlayer     1   class TapeDeck
 2     def turn_on      2     def turn_on
 3       @on = true     3       @on =...
 1   class AudioDevice
 2     def turn_on
 3       @on = true
 4     end
 5
 6     def turn_off
 7       @on = false
 8   ...
Strategy
 1   class Text
 2     attr_reader :contents
 3
 4     def left_aligned_format
 5       # ...
 6     end
 7
 8     def rig...
 1   class LeftAlignedFormatter
 2     def format(context)
 3       align_left context.contents
 4     end
 5   end
 6
 7 ...
 1   class Text
 2     attr_reader :contents
 3     attr_accessor :formatter
 4
 5     def initialize(contents, formatter)...
Blocos, ao resgate!




http://www.flickr.com/photos/bekahstargazing/
 1   class Text
 2     attr_reader :contents
 3     attr_accessor :formatter
 4
 5     def initialize(contents, &formatter...
Use quando você precisar
 alterar o algorítmo em
   tempo de execução
http://www.flickr.com/photos/micahdowty/
 1   class Document
 2     def change_text_color(color)
 3       @text.color = color
 4     end
 5
 6     def change_backg...
ctrl-z ?
 1   class Command
 2     attr_reader :description
 3
 4     def initialize(description)
 5       @description = descripti...
 1   class ChangeTextColorCommand < Command
 2     def initialize(target)
 3       super("Altera a cor do texto")    
 4  ...
 1   class Document
 2     def initialize
 3       @commands = []
 4     end
 5
 6     def change_text_color(color)
 7    ...
Use quando você precisar
realizar tarefas específicas e
 mantê-las em um registro
Proxy
 1   class Task 
 2     def initialize(creator)
 3       @creator = creator
 4     end
 5
 6     def owner=(owner)
 7     ...
 1   class TaskProtectionProxy
 2     def initialize(target)
 3       @target = target    
 4     end
 5
 6     def owner=...
 1   class TaskProtectionProxy
 2     def initialize(target)
 3       @target = target
 4     end
 5
 6     def method_mis...
Perguntas?
Obrigado!
Upcoming SlideShare
Loading in...5
×

Refatoração + Design Patterns em Ruby

6,822

Published on

Palestra feita no dia 29/05/2010 no evento Ruby e Rails no Mundo Real.

Published in: Technology
3 Comments
29 Likes
Statistics
Notes
No Downloads
Views
Total Views
6,822
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
215
Comments
3
Likes
29
Embeds 0
No embeds

No notes for slide

































































































  • Refatoração + Design Patterns em Ruby

    1. 1. Refatorando Ruby Técnicas de Orientação a Objetos e Design Patterns Aplicados a Linguagens Dinâmicas Cássio Marques Ruby e Rails no Mundo Real 2010
    2. 2. cassiomarques.wordpress.com @cassiomarques cassiommc@gmail.com
    3. 3. http://www.flickr.com/photos/docman/ Refatoração
    4. 4. Motivações
    5. 5. Muitos programadores usando Ruby não sabem usar OO
    6. 6. “Raciocínio estático”
    7. 7. Linguagem nova, mas hábitos antigos
    8. 8. Organização
    9. 9. Modularizar o código
    10. 10. Facilitar manutenção
    11. 11. Facilitar compreensão do código para novos desenvolvedores
    12. 12. Design Patterns
    13. 13. Motivações
    14. 14. Ajudar o Ruby a ser “Enterprise”
    15. 15. Patterns implementados X Enterprisy
    16. 16. YAGNI
    17. 17. Um exemplo simples...
    18. 18.  1 class Bhaskara  2   def solve(a, b, c)  3     # Calcula o delta  4     d = b ** 2 - 4 * a * c  5  6     if d < 0  7       puts 'Raízes complexas - Não sei resolver!'  8     else  9       # calculando raízes 10       r1 = (-b + Math.sqrt(d))/(2.0 * a) 11       r2 = (-b - Math.sqrt(d))/(2.0 * a) 12 13       # imprimindo resultados 14       puts "Raiz 1: #{r1}" 15       puts "Raiz 2: #{r2}" 16     end 17   end 18 end
    19. 19. 1 class Bhaskara   2 end
    20. 20. 1 class Bhaskara 2   def initialize(a, b, c) 3     @a, @b, @c = a, b, c 4   end 5 end
    21. 21.  1 class Bhaskara  2   def initialize(a, b, c)  3     @a, @b, @c = a, b, c  4   end  5  6   private  7   def delta  8     @b ** 2 - 4 * @a * @c      9   end 10 11   def solve 12     @r1 = (-@b + Math.sqrt(delta))/(2.0 * @a) 13     @r2 = (-@b - Math.sqrt(delta))/(2.0 * @a) 14   end 15 end
    22. 22.  1 class Bhaskara  2   def initialize(a, b, c)  3     @a, @b, @c = a, b, c  4   end  5  6   def print_results  7     if delta < 0  8       puts 'Raízes complexas - Não sei resolver!'  9     else 10       solve 11       puts "Raiz 1: #{@r1}" 12       puts "Raiz 2: #{@r2}" 13     end 14   end 15 16   private 17   def delta 18     @b ** 2 - 4 * @a * @c     19   end 20 21   def solve 22     @r1 = (-@b + Math.sqrt(delta))/(2.0 * @a) 23     @r2 = (-@b - Math.sqrt(delta))/(2.0 * @a) 24   end 25 end
    23. 23.  1 class Bhaskara  2   def initialize(a, b, c)  3     @a, @b, @c = a, b, c  4   end  5  6   def print_results  7     delta < 0 ? print_complex_results : print_real_results  8   end  9 10   private 11   def delta 12     @b ** 2 - 4 * @a * @c     13   end 14 15   def solve 16     @r1 = (-@b + Math.sqrt(delta))/(2.0 * @a) 17     @r2 = (-@b - Math.sqrt(delta))/(2.0 * @a) 18   end 19 20   def print_real_results 21     solve 22     puts "Raiz 1: #{@r1}" 23     puts "Raiz 2: #{@r2}" 24   end 25 26   def print_complex_results 27     puts 'Raízes complexas - Não sei resolver!' 28   end 29 end
    24. 24. O que eu preciso para refatorar?
    25. 25. TESTES
    26. 26. Mantenha seus métodos pequenos http://www.flickr.com/photos/davidden/
    27. 27.  1 class ShoppingCart  2   def total_value  3     total = 0  4     for item in @items  5       sub_total = item.value * item.quantity  6       if item.quantity > 3  7         sub_total *= 0.90  8       elsif item.quantity > 6  9         sub_total *= 0.85 10       elsif item.quantiy > 9 11         sub_total *= 0.80 12       end 13       total += sub_total 14     end 15     total 16   end 17 end
    28. 28.  1 # - Elimina variáveis temporárias em favor de métodos  2 class ShoppingCart  3   def total_value  4     total = 0  5     for item in @items  6       total += if item.quantity > 3  7         sub_total(item) * 0.90  8       elsif item.quantity > 6  9         sub_total(item) * 0.85 10       elsif item.quantiy > 9 11         sub_total(item) * 0.80 12       end 14     end 15     total 16   end 17 18   def sub_total(item) 19     item.value * item.quantity 20   end 21 end 22
    29. 29.  1 # - Método usa apenas atributos de outro objeto  2 #  => Mover!  3 class ShoppingCart  4   def total_value  5     total = 0  6     for item in @items  7       total += if item.quantity > 3  8         item.total_value * 0.90  9       elsif item.quantity > 6 10         item_total_value * 0.85 11       elsif item.quantiy > 9 12         item.total_value = 0.80 13       end        14     end 15     total 16   end 17 end 18 19 class Item 20   def total_value 21     @value * @quantity 22   end 23 end
    30. 30.  1 # - Método fazendo coisa demais  2 # => Extrair!  3 class ShoppingCart  4   def total_value  5     total = 0  6     for item in @items  7       total += item.total_value * discount(item.quantity)  8     end  9     total 10   end 11 12   def discount(quantity) 13     case quantity 14     when (1..2); 1.00 15     when (3..5); 0.90 16     when (6..8); 0.85 17     else 0.80     18     end 19   end 20 end
    31. 31.  1 # - Use Ruby!  2 class ShoppingCart  3   def total_value  4     items.inject(0.0) { |sum, item|   5       sum += item.total_value * discount(item.quantity)  6     }  7   end  8  9   def discount(quantity) 10     case quantity 11     when (1..2); 1.00 12     when (3..5); 0.90 13     when (6..8); 0.85 14     else 0.80     15     end 16   end 17 end
    32. 32. Faça objetos se comportarem como coleções http://www.flickr.com/photos/deepsignal/366774303/sizes/l/
    33. 33.  1 class Library  2   def search_book_by_title(title)  3     @books.select { |book| book.title == title }  4   end  5  6   def search_book_by_author(author)  7     @books.select { |book| book.author == author }  8   end  9 10   def sort_by_title 11     @books.sort_by { |book| book.title } 12   end 13 14   def authors 15     @books.map { |book| book.author } 16   end 17 18   # ... 19 end
    34. 34.  1 class Library  2   include Enumerable  3  4   def each  5     @books.each { |book| yield book }  6   end  7 end  8  9 library.each { |book| ... } 10 library.inject { |book| ... } 11 library.map { |book| ... } 12 library.sort { |book| ... } 13 # ...
    35. 35. Dê nomes aos parâmetros dos métodos http://www.flickr.com/photos/giantginkgo/
    36. 36.  1 class Dialog  2   def initialize(height, width, name, title)  3     @height = height  4     @width  = width  5     @name   = name  6     @title  = title  7   end  8 end  9 10 dialog = Dialog.new 200, 400, 'dialog1', 'A Dialog'
    37. 37.  1 class Dialog  2   def initialize(options)  3     @height = options[:height]  4     @width  = options[:width]  5     @name   = options[:name]  6     @title  = options[:title]  7   end  8 end  9 10 dialog = Dialog.new( 11   :height => 200, 12   :width  => 400, 13   :name   => 'dialog1', 14   :title  => 'A Dialog' 15 )
    38. 38. Anotações http://www.flickr.com/photos/matski_98/
    39. 39.  1 class Person  2   def state=(state)  3     unless [:sick, :healthy].include?(state)  4       raise ArgumentError, "unknown state: #{state}"   5     end  6     @state = state  7   end  8 end  9 10 class Vehicle 11   def state=(state) 12     unless [:parked, :running].include?(state) 13       raise ArgumentError, "unknown state: #{state}"  14     end 15     @state = state 16   end 17 end
    40. 40.  1 module HasStates  2   def has_states(*args)  3     define_method :state= do |new_state|  4       unless args.include?(new_state)  5         raise ArgumentError, "unknown state: #{state}"   6       end  7       @state = new_state  8     end  9   end 10 end
    41. 41.  1 class Person  2   extend HasStates  3  4   has_states :sick, :healthy  5 end  6  7 class Vehicle  8   extend HasStates  9 10   has_states :parked, :running 11 end 12 13 Object.extend HasStates
    42. 42. Uma classe não deve realizar trabalhos que não lhe pertencem...
    43. 43. 1 class Person 2   attr_accessor :telephone_number, :telephone_area_code 3 4   def formatted_telephone 5     "(#{telephone_area_code}) #{telephone_number}" 6   end 7 end
    44. 44.  1 class Person  2   def initialize  3     @telephone_number = TelephoneNumber.new  4   end  5 end  6  7 class TelephoneNumber  8   attr_accessor :area_code, :number  9 10   def formatted 11     "(#{area_code}) #{number}" 12   end 13 end
    45. 45. Delegue, delegue, delegue...
    46. 46.  1 class Queue  2   def initialize  3     @queue = []  4   end  5  6   def enqueue(element)  7     @queue.unshift element      8   end  9 10   def dequeue 11     @queue.pop 12   end 13 end
    47. 47. O Ruby pode dar uma ajudinha...
    48. 48.  1 require 'forwardable'  2  3 class Queue  4   extend Forwardable  5  6   def initialize  7     @queue = []  8   end  9 10   def_delegator :@queue, :unshift, :enqueue 11   def_delegator :@queue, :pop, :dequeue 12 end
    49. 49. Não exponha o interior de seus objetos sem necessidade http://www.flickr.com/photos/mwichary/
    50. 50.  1 class Room  2   attr_reader :air_conditioner  3 end  4  5 class AirConditioner  6   attr_reader :sensor  7 end  8  9 class TemperatureSensor 10   def temperature 11     #... 12   end 13 end 14 15 if room.air_conditioner.sensor.temperature > 28 16   room.air_conditioner.on 17 end
    51. 51.  1 class Room  2   attr_reader :air_conditioner  3  4   def temperature  5     air_conditioner.sensor.temperature  6   end  7 end  8  9 if room.temperature > 28 10   room.air_conditioner.on 11 end
    52. 52. Substitua números mágicos por constantes http://decluttr.com/4524127771_white
    53. 53. 1 class Circle 2   def area 3     3.14159265 * (r ** 2) 4   end 5 end
    54. 54. 1 class Circle 2   PI = 3.14159265  3 4   def area 5     PI * (r ** 2) 6   end 7 end
    55. 55. Encapsule coleções de objetos http://www.flickr.com/photos/eagleglide/
    56. 56.  1 class Cart  2   attr_accessor :products  3 end  4  5 class Product  6   attr_reader :value, :name  7  8   def initialize(name, value)  9     @name, @value = name, value 10   end 11 end
    57. 57. 1 products = [] 2 products << Product.new('Camiseta', 35.00) 3 products << Product.new('Bermuda', 38.00) 4 products << Product.new('Boné', 20.00) 5 cart = Cart.new 6 cart.products = products 7 cart.products.pop # => alterei o estado do carrinho
    58. 58.  1 class Cart  2   def initialize  3     @products = []  4   end  5  6   def add_product(product)  7     @products << product      8   end  9 10   def remove_product(product) 11     @products.delete product 12   end 13 end 14 15 cart = Cart.new 16 cart.add_product(Product.new('Camiseta', 35.00))
    59. 59. Substitua condicionais por polimorfismo http://www.flickr.com/photos/randyread/
    60. 60.  1 class Tax  2   def initialize(type, value)  3     @type = type      4     @value = value  5   end  6  7   def retained_value  8     case @type  9     when :irpj;   @value * 0.015 10     when :pis;    @value * 0.0065 11     when :cofins; @value * 0.030 12     when :iss;    0.0 13     end 14   end 15 16   def due_value 17     case @type 18     when :irpj;   @value * 0.033 19     when :pis;    0.0 20     when :cofins; 0.0 21     when :iss;    @value * 0.02 22     end 23   end 24 end
    61. 61.  1 module Tax  2   def due_value  3     @due_rate * @value      4   end  5  6   def retained_value  7     @retained_rate * @value  8   end  9 end 10 11 class IrpjTax 12   include Tax 13 14   def initialize(value) 15     @value         = value 16     @due_rate      = 0.033 17     @retained_rate = 0.015 18   end 19 end 20 21 # o mesmo para as demais classes...
    62. 62.  1 class Invoice  2   def initialize(value)  3     @value = value  4     create_taxes  5   end  6  7   def add_tax(tax)  8     @taxes << tax  9   end 10    11   def total_taxes_value 12     @taxes.inject(0.0) { |sum, tax| 13       sum += tax.due_value + tax.retained_value 14     } 15   end 16 17   private 18   def create_taxes 19     @taxes = [] 20     @taxes << IrpjTax.new(@value) 21     @taxes << PisTax.new(@value) 22     @taxes << CofinsTax.new(@value) 23     @taxes << IssTax.new(@value) 24   end 25 end 26 27 invoice = Invoice.new 1400.00 28 puts invoice.total_taxes_value
    63. 63. Simplifique expressões condicionais
    64. 64. 1 if product.release_date < 3.months.ago || quantity > 100 2   self.value *= 0.90 3 end
    65. 65.  1 if elegible_for_discount?  2   self.value *= 0.90  3 end  4  5 def elegible_for_discount?  6   old? && great_quantity?  7 end  8  9 def old? 10   product.release_date < 3.months.ago 11 end 12 13 def great_quantity? 14   quantity > 100 15 end
    66. 66. http://www.flickr.com/photos/jeff_oliver/ Mas uma expressão condicional pode ser melhor que ifs aninhados...
    67. 67. 1 if age > 40 2   if gender == :male 3     if last_visit > 1.month.ago 4       # ... 5     end 6   end 7 end
    68. 68. 1 if age > 40 && gender == :male && last_visit > 1.month 2   # ... 3 end 4 5 # e a partir daqui podemos extrair as condições para 6 # métodos...
    69. 69. Às vezes faz mais sentido juntar do que separar...
    70. 70. 1 class Bicycle 2   def press_front_brake 3     brakes[:front].press 4   end 5 6   def press_rear_brake 7     brakes[:rear].press 8   end 9 end
    71. 71. 1 class Bicycle 2   def press_brake(brake) 3     brakes[brake].press 4   end 5 end
    72. 72. Verifique se a mensagem pode ser enviada http://www.flickr.com/photos/funtik/
    73. 73. 1 def do_something(target) 2   target.prepare rescue nil 3   target.explode 4 end
    74. 74. 1 def do_something(target) 2   target.prepare if target.respond_to? :prepare 3   target.explode 4 end
    75. 75. Herança
    76. 76.  1 class CdPlayer  1 class TapeDeck  2   def turn_on  2   def turn_on  3     @on = true  3     @on = true  4   end  4   end  5  5  6   def turn_off  6   def turn_off  7     @on = false  7     @on = false  8   end  8   end  9  9 10   def play 10   def play 11     play_disc 11     play_tape 12   end 12   end 13 end 13 end
    77. 77.  1 class AudioDevice  2   def turn_on  3     @on = true  4   end  5  6   def turn_off  7     @on = false  8   end  9 end 10 11 class CdPlayer < AudioDevice 12   def play 13     play_disk 14   end 15 end 16 17 class TapeDeck < AudioDevice 18   def play 19     play_tape 20   end 21 end
    78. 78. Strategy
    79. 79.  1 class Text  2   attr_reader :contents  3  4   def left_aligned_format  5     # ...  6   end  7  8   def right_aligned_format  9     # ... 10   end 11 12   def justified_format 13     # ... 14   end 15 end
    80. 80.  1 class LeftAlignedFormatter  2   def format(context)  3     align_left context.contents  4   end  5 end  6  7 class RightAlignedFormatter  8   def format(context)  9     align_right context.contents 10   end 11 end 12 13 class RightJustifiedFormatter  14   def format(context) 15     justify context.contents 16   end 17 end
    81. 81.  1 class Text  2   attr_reader :contents  3   attr_accessor :formatter  4  5   def initialize(contents, formatter)  6     @contents, @formatter = contents, formatter  7   end  8  9   def format 10     @formatter.format self 11   end 12 end
    82. 82. Blocos, ao resgate! http://www.flickr.com/photos/bekahstargazing/
    83. 83.  1 class Text  2   attr_reader :contents  3   attr_accessor :formatter  4  5   def initialize(contents, &formatter)  6     @contents, @formatter = contents, formatter  7   end  8  9   def format 10     @formatter.call self 11   end 12 end 13 14 left_aligned_text = Text.new 'some text' do |context| 15   align_left context.contents 16 end
    84. 84. Use quando você precisar alterar o algorítmo em tempo de execução
    85. 85. http://www.flickr.com/photos/micahdowty/
    86. 86.  1 class Document  2   def change_text_color(color)  3     @text.color = color  4   end  5  6   def change_background_color(color)  7     @text.background_color = color      8   end  9 10   def change_font_size(size) 11     @text.font_size = size     12   end 13 14   def change_font(font) 15     @text.font = font 16   end 17 end
    87. 87. ctrl-z ?
    88. 88.  1 class Command  2   attr_reader :description  3  4   def initialize(description)  5     @description = description      6   end  7 end
    89. 89.  1 class ChangeTextColorCommand < Command  2   def initialize(target)  3     super("Altera a cor do texto")      4     @target = target  5   end  6  7   def execute(color)  8     @old_color = @target.text.color  9     @target.text.color = color 10   end   11 12   def unexecute 13     @target.text.color = @old_color if @old_color 14   end 15 end
    90. 90.  1 class Document  2   def initialize  3     @commands = []  4   end  5  6   def change_text_color(color)  7     run_command ChangeTextColorCommand.new(self), color  8   end  9 10   def change_font(font) 11     run_command ChangeFontCommand.new(self), font 12   end 13 14   def undo_last_action 15     @commands.pop.unexecute unless @commands.empty? 16   end 17 18   private 19   def run_command(command, param) 20     command.execute param 21     @commands << command 22   end 23 end
    91. 91. Use quando você precisar realizar tarefas específicas e mantê-las em um registro
    92. 92. Proxy
    93. 93.  1 class Task   2   def initialize(creator)  3     @creator = creator  4   end  5  6   def owner=(owner)  7     @owner = owner      8   end  9 10   def add_comment(comment) 11     @comments << comment 12   end 13 14   def update_description(new_description) 15     @description = description 16   end 17 18   def close 19     @status = :closed 20   end 21 end
    94. 94.  1 class TaskProtectionProxy  2   def initialize(target)  3     @target = target      4   end  5  6   def owner=(owner)  7     check_permissions :owner=  8     @owner = owner      9   end 10 11   def add_comment(comment)     12     check_permissions :add_comment 13     @comments << comment 14   end 15 16   # ... 17 18   private 19   def check_permissions(what) 20     unless can_do?(current_user, what) 21       raise 'Acesso negado!'  22     end 23   end 24 end
    95. 95.  1 class TaskProtectionProxy  2   def initialize(target)  3     @target = target  4   end  5  6   def method_missing(method, *args)  7     check_permissions(method)  8     @target.send method, args  9   end 10 11   private  12   def check_permissions(what) 13 unless can_do?(current_user, what) 14       raise 'Acesso negado!'  15     end 16   end 17 end
    96. 96. Perguntas?
    97. 97. Obrigado!
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×