SlideShare a Scribd company logo
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
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 < 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
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 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
 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
 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
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.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
 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
 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
 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
 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
Faça objetos se comportarem
       como coleções




                   http://www.flickr.com/photos/deepsignal/366774303/sizes/l/
 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
 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   # ...
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       @name   = name
 6       @title  = title
 7     end
 8   end
 9
10   dialog = Dialog.new 200, 400, 'dialog1', 'A Dialog'
 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   )
Anotações




http://www.flickr.com/photos/matski_98/
 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
 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
 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
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       "(#{telephone_area_code}) #{telephone_number}"
6     end
7   end
 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
Delegue, delegue, delegue...
 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
O Ruby pode dar uma
     ajudinha...
 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
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   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
 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
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/eagleglide/
 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
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
 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))
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     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
 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...
 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
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_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
http://www.flickr.com/photos/jeff_oliver/




                                                 Mas uma expressão
                                                condicional pode ser
                                           melhor que ifs aninhados...
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 condições para
6   # métodos...
À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       brakes[:rear].press
8     end
9   end
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 = 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
 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
Strategy
 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
 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
 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
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)
 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
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_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
ctrl-z ?
 1   class Command
 2     attr_reader :description
 3
 4     def initialize(description)
 5       @description = description  
                                     
 6     end
 7   end
 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
 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
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       @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
 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
 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
Perguntas?
Obrigado!
Ad

Recommended

PPTX
JavaScript Proven Practises
Robert MacLean
 
PDF
PythonOOP
Veera Pendyala
 
PDF
The Ring programming language version 1.7 book - Part 35 of 196
Mahmoud Samir Fayed
 
PDF
Статичный SQL в С++14. Евгений Захаров ➠ CoreHard Autumn 2019
corehard_by
 
DOCX
VISUALIZAR REGISTROS EN UN JTABLE
Darwin Durand
 
PDF
Sql server query collection
Rabin Koirala
 
PDF
Specs2
Piyush Mishra
 
PDF
Sorting Alpha Numeric Data in MySQL
Abdul Rahman Sherzad
 
PDF
Taming forms with React
GreeceJS
 
PDF
Ruby closures, how are they possible?
Carlos Alonso Pérez
 
PPT
Oracle naveen Sql
naveen
 
PDF
The Ring programming language version 1.8 book - Part 32 of 202
Mahmoud Samir Fayed
 
DOC
Oracle
Rajeev Uppala
 
PDF
Ruby Meetup Balabit
Papp Laszlo
 
PPTX
Operator overload rr
Dhivya Shanmugam
 
PDF
The Ring programming language version 1.10 book - Part 103 of 212
Mahmoud Samir Fayed
 
PDF
Java script introducation & basics
H K
 
PPT
Jhtp5 20 Datastructures
martha leon
 
PDF
[Www.pkbulk.blogspot.com]dbms06
AnusAhmad
 
PDF
MariaDB Optimizer - further down the rabbit hole
Sergey Petrunya
 
KEY
Active Record Query Interface (1), Season 2
RORLAB
 
PDF
To be Continued - multithreading with Project Loom and Kotlin's Coroutines
Artur Skowroński
 
PDF
New Query Optimizer features in MariaDB 10.3
Sergey Petrunya
 
PPT
Les01-Oracle
suman1248
 
PDF
Swift에서 꼬리재귀 사용기 (Tail Recursion)
진성 오
 
PDF
The Ring programming language version 1.6 book - Part 29 of 189
Mahmoud Samir Fayed
 
ODP
Solr facets and custom indices
cgmonroe
 
ODP
Building a Hierarchical Data Model Using the Latest IBM Informix Features
Ajay Gupte
 
KEY
Design Patterns on Rails
tchandy
 
PDF
Go for Rubyists
tchandy
 

More Related Content

What's hot (20)

PDF
Taming forms with React
GreeceJS
 
PDF
Ruby closures, how are they possible?
Carlos Alonso Pérez
 
PPT
Oracle naveen Sql
naveen
 
PDF
The Ring programming language version 1.8 book - Part 32 of 202
Mahmoud Samir Fayed
 
DOC
Oracle
Rajeev Uppala
 
PDF
Ruby Meetup Balabit
Papp Laszlo
 
PPTX
Operator overload rr
Dhivya Shanmugam
 
PDF
The Ring programming language version 1.10 book - Part 103 of 212
Mahmoud Samir Fayed
 
PDF
Java script introducation & basics
H K
 
PPT
Jhtp5 20 Datastructures
martha leon
 
PDF
[Www.pkbulk.blogspot.com]dbms06
AnusAhmad
 
PDF
MariaDB Optimizer - further down the rabbit hole
Sergey Petrunya
 
KEY
Active Record Query Interface (1), Season 2
RORLAB
 
PDF
To be Continued - multithreading with Project Loom and Kotlin's Coroutines
Artur Skowroński
 
PDF
New Query Optimizer features in MariaDB 10.3
Sergey Petrunya
 
PPT
Les01-Oracle
suman1248
 
PDF
Swift에서 꼬리재귀 사용기 (Tail Recursion)
진성 오
 
PDF
The Ring programming language version 1.6 book - Part 29 of 189
Mahmoud Samir Fayed
 
ODP
Solr facets and custom indices
cgmonroe
 
ODP
Building a Hierarchical Data Model Using the Latest IBM Informix Features
Ajay Gupte
 
Taming forms with React
GreeceJS
 
Ruby closures, how are they possible?
Carlos Alonso Pérez
 
Oracle naveen Sql
naveen
 
The Ring programming language version 1.8 book - Part 32 of 202
Mahmoud Samir Fayed
 
Ruby Meetup Balabit
Papp Laszlo
 
Operator overload rr
Dhivya Shanmugam
 
The Ring programming language version 1.10 book - Part 103 of 212
Mahmoud Samir Fayed
 
Java script introducation & basics
H K
 
Jhtp5 20 Datastructures
martha leon
 
[Www.pkbulk.blogspot.com]dbms06
AnusAhmad
 
MariaDB Optimizer - further down the rabbit hole
Sergey Petrunya
 
Active Record Query Interface (1), Season 2
RORLAB
 
To be Continued - multithreading with Project Loom and Kotlin's Coroutines
Artur Skowroński
 
New Query Optimizer features in MariaDB 10.3
Sergey Petrunya
 
Les01-Oracle
suman1248
 
Swift에서 꼬리재귀 사용기 (Tail Recursion)
진성 오
 
The Ring programming language version 1.6 book - Part 29 of 189
Mahmoud Samir Fayed
 
Solr facets and custom indices
cgmonroe
 
Building a Hierarchical Data Model Using the Latest IBM Informix Features
Ajay Gupte
 

Viewers also liked (19)

KEY
Design Patterns on Rails
tchandy
 
PDF
Go for Rubyists
tchandy
 
PDF
Abertura do ruby_rails_no_mundo_real_guru_sp
Willian Molinari
 
PDF
Ruby + Rails no Mundo Real 2010 - Voodoo é pra Jacu - Entendendo metaprograma...
David Paniz
 
PPTX
Aplicacoes para celular com Ruby
mfcastellani
 
PDF
batch - escalando um sistema sem fermento
Douglas Campos
 
PDF
O que há de novo no Rails 3
Hugo Baraúna
 
ODP
BDD & Cucumber
andersonleite
 
TXT
창원오피톡 창원오피방 창원오피 오피톡[ optok4] 창원op 창원건마 창원건전마사지
tok opop
 
PDF
Calendrier des activités de JEADER _ AFRIQUE _ 2016
JEADER
 
PDF
Product Cost Analytics solution overview
Sridhar Pai
 
PDF
Portadas nacionales 22 marzo-17
Portadas Nacionales Think Mercadotecnia
 
PDF
East Coast Transport-MACRO POINT CASE STUDY
Paul Berman
 
PPTX
Los 12 mandamientos del periodista
IgnacioRamosMancheno
 
PDF
Women in Islam & Refutation of some Common Misconceptions
Islamic Invitation
 
PPTX
Sri Lankan Tours
ranjithalwis
 
PDF
employee-awareness-and-training-the-holy-grail-of-cybersecurity
Paul Ferrillo
 
PDF
A Administração e a Contabilidade : Uma relação que gera vantagem competitiva...
Cra-es Conselho
 
Design Patterns on Rails
tchandy
 
Go for Rubyists
tchandy
 
Abertura do ruby_rails_no_mundo_real_guru_sp
Willian Molinari
 
Ruby + Rails no Mundo Real 2010 - Voodoo é pra Jacu - Entendendo metaprograma...
David Paniz
 
Aplicacoes para celular com Ruby
mfcastellani
 
batch - escalando um sistema sem fermento
Douglas Campos
 
O que há de novo no Rails 3
Hugo Baraúna
 
BDD & Cucumber
andersonleite
 
창원오피톡 창원오피방 창원오피 오피톡[ optok4] 창원op 창원건마 창원건전마사지
tok opop
 
Calendrier des activités de JEADER _ AFRIQUE _ 2016
JEADER
 
Product Cost Analytics solution overview
Sridhar Pai
 
Portadas nacionales 22 marzo-17
Portadas Nacionales Think Mercadotecnia
 
East Coast Transport-MACRO POINT CASE STUDY
Paul Berman
 
Los 12 mandamientos del periodista
IgnacioRamosMancheno
 
Women in Islam & Refutation of some Common Misconceptions
Islamic Invitation
 
Sri Lankan Tours
ranjithalwis
 
employee-awareness-and-training-the-holy-grail-of-cybersecurity
Paul Ferrillo
 
A Administração e a Contabilidade : Uma relação que gera vantagem competitiva...
Cra-es Conselho
 
Ad

Similar to Refatoração + Design Patterns em Ruby (20)

PDF
Ruby - Design patterns tdc2011
Rafael Felix da Silva
 
PDF
Refactoring Ruby Code
Caike Souza
 
PDF
Refactoring Workshop (Rails Pacific 2014)
Bruce Li
 
PDF
Refactoring
Caike Souza
 
PDF
Ruby - Uma Introdução
Ígor Bonadio
 
PDF
Impacta - Show Day de Rails
Fabio Akita
 
KEY
Refactor like a boss
gsterndale
 
KEY
Ruby 2.0: to infinity... and beyond!
Fabio Kung
 
PPTX
Ruby object model
Chamnap Chhorn
 
PDF
Ruby 101
Harisankar P S
 
PDF
Design Patterns the Ruby way - ConFoo 2015
Fred Heath
 
PDF
Introduction to Ruby Programming Language
Nicolò Calcavecchia
 
PDF
Ruby Metaprogramming
Nando Vieira
 
ODP
Ruby Basics by Rafiq
Rafiqdeen
 
PDF
Rails workshop for Java people (September 2015)
Andre Foeken
 
PDF
Metaprogramming 101
Nando Vieira
 
KEY
An introduction to Ruby
Wes Oldenbeuving
 
PPT
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
Anton Shemerey
 
PDF
Ruby on Rails
bryanbibat
 
KEY
Desarrollando aplicaciones web en minutos
Edgar Suarez
 
Ruby - Design patterns tdc2011
Rafael Felix da Silva
 
Refactoring Ruby Code
Caike Souza
 
Refactoring Workshop (Rails Pacific 2014)
Bruce Li
 
Refactoring
Caike Souza
 
Ruby - Uma Introdução
Ígor Bonadio
 
Impacta - Show Day de Rails
Fabio Akita
 
Refactor like a boss
gsterndale
 
Ruby 2.0: to infinity... and beyond!
Fabio Kung
 
Ruby object model
Chamnap Chhorn
 
Ruby 101
Harisankar P S
 
Design Patterns the Ruby way - ConFoo 2015
Fred Heath
 
Introduction to Ruby Programming Language
Nicolò Calcavecchia
 
Ruby Metaprogramming
Nando Vieira
 
Ruby Basics by Rafiq
Rafiqdeen
 
Rails workshop for Java people (September 2015)
Andre Foeken
 
Metaprogramming 101
Nando Vieira
 
An introduction to Ruby
Wes Oldenbeuving
 
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
Anton Shemerey
 
Ruby on Rails
bryanbibat
 
Desarrollando aplicaciones web en minutos
Edgar Suarez
 
Ad

Recently uploaded (20)

PPTX
Security Tips for Enterprise Azure Solutions
Michele Leroux Bustamante
 
PDF
GenAI Opportunities and Challenges - Where 370 Enterprises Are Focusing Now.pdf
Priyanka Aash
 
PDF
Salesforce Summer '25 Release Frenchgathering.pptx.pdf
yosra Saidani
 
PDF
PyCon SG 25 - Firecracker Made Easy with Python.pdf
Muhammad Yuga Nugraha
 
PPTX
Wenn alles versagt - IBM Tape schützt, was zählt! Und besonders mit dem neust...
Josef Weingand
 
PDF
Mastering AI Workflows with FME by Mark Döring
Safe Software
 
PDF
Oh, the Possibilities - Balancing Innovation and Risk with Generative AI.pdf
Priyanka Aash
 
PDF
A Constitutional Quagmire - Ethical Minefields of AI, Cyber, and Privacy.pdf
Priyanka Aash
 
PDF
Using the SQLExecutor for Data Quality Management: aka One man's love for the...
Safe Software
 
PDF
Quantum AI: Where Impossible Becomes Probable
Saikat Basu
 
PDF
Lessons Learned from Developing Secure AI Workflows.pdf
Priyanka Aash
 
PDF
9-1-1 Addressing: End-to-End Automation Using FME
Safe Software
 
PDF
WebdriverIO & JavaScript: The Perfect Duo for Web Automation
digitaljignect
 
PDF
"Scaling in space and time with Temporal", Andriy Lupa.pdf
Fwdays
 
PDF
"Database isolation: how we deal with hundreds of direct connections to the d...
Fwdays
 
PPTX
OpenACC and Open Hackathons Monthly Highlights June 2025
OpenACC
 
PDF
Smarter Aviation Data Management: Lessons from Swedavia Airports and Sweco
Safe Software
 
PDF
Connecting Data and Intelligence: The Role of FME in Machine Learning
Safe Software
 
PDF
Python Conference Singapore - 19 Jun 2025
ninefyi
 
PDF
Agentic AI for Developers and Data Scientists Build an AI Agent in 10 Lines o...
All Things Open
 
Security Tips for Enterprise Azure Solutions
Michele Leroux Bustamante
 
GenAI Opportunities and Challenges - Where 370 Enterprises Are Focusing Now.pdf
Priyanka Aash
 
Salesforce Summer '25 Release Frenchgathering.pptx.pdf
yosra Saidani
 
PyCon SG 25 - Firecracker Made Easy with Python.pdf
Muhammad Yuga Nugraha
 
Wenn alles versagt - IBM Tape schützt, was zählt! Und besonders mit dem neust...
Josef Weingand
 
Mastering AI Workflows with FME by Mark Döring
Safe Software
 
Oh, the Possibilities - Balancing Innovation and Risk with Generative AI.pdf
Priyanka Aash
 
A Constitutional Quagmire - Ethical Minefields of AI, Cyber, and Privacy.pdf
Priyanka Aash
 
Using the SQLExecutor for Data Quality Management: aka One man's love for the...
Safe Software
 
Quantum AI: Where Impossible Becomes Probable
Saikat Basu
 
Lessons Learned from Developing Secure AI Workflows.pdf
Priyanka Aash
 
9-1-1 Addressing: End-to-End Automation Using FME
Safe Software
 
WebdriverIO & JavaScript: The Perfect Duo for Web Automation
digitaljignect
 
"Scaling in space and time with Temporal", Andriy Lupa.pdf
Fwdays
 
"Database isolation: how we deal with hundreds of direct connections to the d...
Fwdays
 
OpenACC and Open Hackathons Monthly Highlights June 2025
OpenACC
 
Smarter Aviation Data Management: Lessons from Swedavia Airports and Sweco
Safe Software
 
Connecting Data and Intelligence: The Role of FME in Machine Learning
Safe Software
 
Python Conference Singapore - 19 Jun 2025
ninefyi
 
Agentic AI for Developers and Data Scientists Build an AI Agent in 10 Lines o...
All Things Open
 

Refatoração + Design Patterns em Ruby

  • 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
  • 5. Muitos programadores usando Ruby não sabem usar OO
  • 7. Linguagem nova, mas hábitos antigos
  • 11. Facilitar compreensão do código para novos desenvolvedores
  • 14. Ajudar o Ruby a ser “Enterprise”
  • 16. YAGNI
  • 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
  • 20. 1 class Bhaskara 2   def initialize(a, b, c) 3     @a, @b, @c = a, b, c 4   end 5 end
  • 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.  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.  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. O que eu preciso para refatorar?
  • 26. Mantenha seus métodos pequenos http://www.flickr.com/photos/davidden/
  • 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.  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.  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.  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.  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. Faça objetos se comportarem como coleções http://www.flickr.com/photos/deepsignal/366774303/sizes/l/
  • 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.  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. Dê nomes aos parâmetros dos métodos http://www.flickr.com/photos/giantginkgo/
  • 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.  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 )
  • 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.  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.  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. Uma classe não deve realizar trabalhos que não lhe pertencem...
  • 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.  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
  • 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. O Ruby pode dar uma ajudinha...
  • 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. Não exponha o interior de seus objetos sem necessidade http://www.flickr.com/photos/mwichary/
  • 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.  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. Substitua números mágicos por constantes http://decluttr.com/4524127771_white
  • 53. 1 class Circle 2   def area 3     3.14159265 * (r ** 2) 4   end 5 end
  • 54. 1 class Circle 2   PI = 3.14159265  3 4   def area 5     PI * (r ** 2) 6   end 7 end
  • 55. Encapsule coleções de objetos http://www.flickr.com/photos/eagleglide/
  • 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. 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.  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. Substitua condicionais por polimorfismo http://www.flickr.com/photos/randyread/
  • 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.  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.  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. Simplifique expressões condicionais
  • 64. 1 if product.release_date < 3.months.ago || quantity > 100 2   self.value *= 0.90 3 end
  • 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. http://www.flickr.com/photos/jeff_oliver/ Mas uma expressão condicional pode ser melhor que ifs aninhados...
  • 67. 1 if age > 40 2   if gender == :male 3     if last_visit > 1.month.ago 4       # ... 5     end 6   end 7 end
  • 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. Às vezes faz mais sentido juntar do que separar...
  • 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. 1 class Bicycle 2   def press_brake(brake) 3     brakes[brake].press 4   end 5 end
  • 72. Verifique se a mensagem pode ser enviada http://www.flickr.com/photos/funtik/
  • 73. 1 def do_something(target) 2   target.prepare rescue nil 3   target.explode 4 end
  • 74. 1 def do_something(target) 2   target.prepare if target.respond_to? :prepare 3   target.explode 4 end
  • 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.  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
  • 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.  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.  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
  • 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. Use quando você precisar alterar o algorítmo em tempo de execução
  • 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
  • 88.  1 class Command  2   attr_reader :description  3  4   def initialize(description)  5     @description = description      6   end  7 end
  • 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.  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. Use quando você precisar realizar tarefas específicas e mantê-las em um registro
  • 92. Proxy
  • 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.  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.  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