Upload
rafael-felix-da-silva
View
2.522
Download
1
Embed Size (px)
DESCRIPTION
Apresentação Sobre Design Patterns na trilha de Ruby/Python do TDC 2011 Floripa
Citation preview
Design Patterns + Ruby
http://github.com/fellix
@rs_felix
http://www.crafters.com.br @crafterstudio
http://blog.rollingwithcode.com
Por que Ruby?
1995
Erich Gamma, Richard Helm, Raph Johnson, John Vlissides
“Gang of Four”
Design Patterns
Template Method
class Relatorio def generate output_head output_body output_footer end def output_head end def output_body end def output_footer endend
class Relatorio def generate output_head output_body output_footer end def output_head end def output_body end def output_footer endend
class Relatorio def generate output_head output_body output_footer end def output_head end def output_body end def output_footer endend
Template Method
class HTMLRelatorio < Relatorio def output_head
puts "<html><head><title>Relatório HTML</title></head>"
end def output_body puts "<body>...</body>" end def output_footer puts "</html>" endend
class HTMLRelatorio < Relatorio def output_head
puts "<html><head><title>Relatório HTML</title></head>"
end def output_body puts "<body>...</body>" end def output_footer puts "</html>" endend
class HTMLRelatorio < Relatorio def output_head
puts "<html><head><title>Relatório HTML</title></head>"
end def output_body puts "<body>...</body>" end def output_footer puts "</html>" endend
relatorio = HTMLRelatorio.newrelatorio.generate
relatorio = HTMLRelatorio.newrelatorio.generate
relatorio = HTMLRelatorio.newrelatorio.generate
class Relatorio def generate output_head output_body output_footer end end
relatorio = HTMLRelatorio.newrelatorio.generate
<html> <head><title>...</title></head> <body> ... </body></html>
Strategy
Delegar ao invés de herdar
class Formatter def format(text)
endend
class Formatter def format(text)
raise "Abstract method" endend
class HTMLFormatter < Formatter def format(text)
puts "<html> " puts "<head> " puts "<title>...</title></head> " puts "<body>#{text}</body></html>"
endend
class HTMLFormatter < Formatter def format(text)
puts "<html> " puts "<head> " puts "<title>...</title></head> " puts "<body>#{text}</body></html>"
endend
class HTMLFormatter def format(text)
puts "<html> " puts "<head> " puts "<title>...</title></head> " puts "<body>#{text}</body></html>"
endend
class HTMLFormatter def format(text)
puts "<html> " puts "<head> " puts "<title>...</title></head> " puts "<body>#{text}</body></html>"
endend
Duck Typing
class Relatorio def initialize(formatter)
@formatter = formatter end def generate(text)
@formatter.format(text) endend
class Relatorio def initialize(formatter)
@formatter = formatter end def generate(text)
@formatter.format(text) endend
class Relatorio def initialize(formatter)
@formatter = formatter end def generate(text)
@formatter.format(text) endend
class Relatorio def initialize(formatter)
@formatter = formatter end def generate(text)
@formatter.format(text) endend
Delegate
relatorio = Relatorio.new(HTMLFormatter.new)relatorio.generate("Muitas e muitas coisas")
relatorio = Relatorio.new(HTMLFormatter.new)relatorio.generate("Muitas e muitas coisas")
relatorio = Relatorio.new(HTMLFormatter.new)relatorio.generate("Muitas e muitas coisas")
Observer
Manter-se informado sobre determinadas mudanças em objetos
class NotaFiscal def pagar
... endend
class FluxoCaixa def atualizar
... endend
class FluxoCaixa def atualizar
... endend
Consulta no banco de dados todas as
notas pagas e atualiza o fluxo de
caixa
NotaFiscal FluxoCaixa
pagar
class NotaFiscal def pagar
... endend
class NotaFiscal def pagar (fluxo)
...fluxo.atualizar
endend
class NotaFiscal def initialize (...)
...@observers = []
end def pagar
... endend
class NotaFiscal def add_observer (observer)
@observers << observer end def notify_observers
@observers.each do |o| o.update(self)
end endend
class FluxoCaixa def atualizar
... end def update (nota_fiscal)
... endend
fluxo_caixa = FluxoCaixa.newnota_fiscal = NotaFiscal.newnota_fiscal.add_observer fluxo_caixa
nota_fiscal.pagar
require "observer" class NotaFiscal
include Observable
def pagar...changednotify_observers(self)
endend
require "observer" class NotaFiscal
include Observable
def pagar...changednotify_observers(self)
endend
require "observer" class NotaFiscal
include Observable
def pagar...changednotify_observers(self)
endend
require "observer" class NotaFiscal
include Observable
def pagar...changednotify_observers(self)
endend
require "observer" class NotaFiscal
include Observable
def pagar...changednotify_observers(self)
endend
@changed = true
require "observer" class NotaFiscal
include Observable
def pagar...changednotify_observers(self)
endend
@observers.each { |o| o.update(object) } if @changed
class FluxoCaixa def atualizar
... end def update (nota_fiscal)
... endend
fluxo_caixa = FluxoCaixa.newnota_fiscal = NotaFiscal.newnota_fiscal.add_observer fluxo_caixa
nota_fiscal.pagar
fluxo_caixa = FluxoCaixa.newnota_fiscal = NotaFiscal.newnota_fiscal.add_observer fluxo_caixa
nota_fiscal.pagar
nota_fiscal fluxo_caixa
notifica
Factory
Fornecer uma interface para a criação de objetos, sem especificar a classe concreta
HTMLReader.new
PDFReader.new
class ReaderFactory def initialize (format)
@reader_class = self.class.const_get("#{format}Reader")
end def reader
@reader_class.new endend
class ReaderFactory def initialize (format)
@reader_class = self.class.const_get("#{format}Reader")
end def reader
@reader_class.new endend
class ReaderFactory def initialize (format)
@reader_class = self.class.const_get("#{format}Reader")
end def reader
@reader_class.new endend
class ReaderFactory def initialize (format)
@reader_class = self.class.const_get("#{format}Reader")
end def reader
@reader_class.new endend
html_reader = ReaderFactory.new("HTML").reader
pdf_reader = ReaderFactory.new("PDF").reader
class ReaderFactory def self.pdf_reader
ReaderFactory.new("PDF").reader end def self.html_reader
ReaderFactory.new("HTML").reader endend
html_reader = ReaderFactory.html_reader
Active Record Factory
class Base ...
end
class Base def self.mysql_connection
... endend
class Base def self.postgresql_connection
... endend
adapter = "mysql"method_name = "#{adapter}_connection"Base.send(method_name, config)
Builder
Criar objetos complexos de forma legível, passo a passo
class Pizza attr_accessor :massa, :molho, :coberturaend
class PizzaBuilder attr_reader :pizza def initialize
@pizza = Pizza.new endend
class CalabresaBuilder < PizzaBuilder def molho
@pizza.molho = "Tomate" end def massa
@pizza.massa = "Pão" end def cobertura
@pizza.cobertura = "queijo, calabresa" endend
builder = CalabresaBuilder.newbuilder.molhobuilder.coberturabuilder.massapizza = builder.pizza
pizza = CalabresaBuilder.new.molho.cobertura.massa.pizza
class CalabresaBuilder < PizzaBuilder def molho
@pizza.molho = "Tomate"self
end def massa
@pizza.massa = "Pão"self
end def cobertura
@pizza.cobertura = "queijo, calabresa"self
endend
builder = CalabresaBuilder.newbuilder.molho_and_coberturapizza = builder.pizza
class PizzaBuilder def method_missing(name, *args)
methods = name.to_s.split("_")return super(name, *args) unless methods[1] == "and"
methods.each do |method|next if method == "and"send(method)
end
endend
builder.molho_and_cobertura
builder.massa_and_cobertura
builder.molho_and_cobertura_and_massa
Adapter
Conectar objetos que não tem uma interface comum
class Padrao1 def conectar (cabo)
cabo.energizar endend
class Padrao1 def conectar (cabo)
cabo.energizar endend
class CaboPadrao1 def energizar
... endend
class Padrao2 def conectar (cabo)
cabo.executar endend
class Padrao2 def conectar (cabo)
cabo.executar endend
class CaboPadrao2 def executar
... endend
class CaboPadrao2 def executar
... endend
class CaboPadrao2 def executar
... endend
tomada = Padrao2.newtomada.conectar CaboPadrao2.new
class CaboPadrao2 def executar
... endend
tomada = Padrao1.newtomada.conectar CaboPadrao2.new
class CaboPadrao2 def executar
... endend
tomada = Padrao1.newtomada.conectar CaboPadrao2.new
class AdapterPadrao2 def initialize(cabo_padrao2)
@cabo = cabo_padrao2 end def energizar
@cabo.executar endend
class AdapterPadrao2 def initialize(cabo_padrao2)
@cabo = cabo_padrao2 end def energizar
@cabo.executar endend
class AdapterPadrao2 def initialize(cabo_padrao2)
@cabo = cabo_padrao2 end def energizar
@cabo.executar endend
adapter = AdapterPadrao2.new(CaboPadrao2.new)tomada = Padrao1.newtomada.conectar(adapter)
adapter = AdapterPadrao2.new(CaboPadrao2.new)tomada = Padrao1.newtomada.conectar(adapter)
adapter = AdapterPadrao2.new(CaboPadrao2.new)tomada = Padrao1.newtomada.conectar(adapter)
tomada adapter cabo
Proxy
class ContaBancaria def initialize(saldo)
@saldo = saldo end def depositar!(valor)
@saldo += valor end def sacar!(valor)
@saldo -= valor end def some_method endend
class ContaBancariaProxy def initialize(conta_bancaria)
@conta_bancaria = conta_bancaria end def depositar!(valor)
@conta_bancaria.depositar! valor end def sacar!(valor)
@conta_bancaria .sacar! valor endend
Protection Proxy
class ContaBancariaProxy def initialize(conta_bancaria)
@conta_bancaria = conta_bancaria end def depositar!(valor)
@conta_bancaria.depositar! valor end def sacar!(valor)
@conta_bancaria .sacar! valor endend
class ContaBancariaProxy def initialize(conta_bancaria, owner)
@conta_bancaria = conta_bancaria@owner = owner
end protected def check_access
raise "Illegal Access" unless User. login == @owner
endend
class ContaBancariaProxy def depositar!(valor) check_access
@conta_bancaria.depositar! valor end def sacar!(valor) check_access
@conta_bancaria .sacar! valor endend
Decorator
class Coffee def price
@price end def ingredients
@ingredients endend
class MilkDecorator def initialize (coffee)
@coffee = coffee end def price
coffee.price + 0.5 end def ingredients
coffee.ingredients + ", Leite" endend
class MilkDecorator def initialize (coffee)
@coffee = coffee end def price
coffee.price + 0.5 end def ingredients
coffee.ingredients + ", Leite" endend
class MilkDecorator def initialize (coffee)
@coffee = coffee end def price
coffee.price + 0.5 end def ingredients
coffee.ingredients + ", Leite" endend
class MilkDecorator def initialize (coffee)
@coffee = coffee end def price
coffee.price + 0.5 end def ingredients
coffee.ingredients + ", Leite" endend
coffee = Coffee.new(1.5, "Café, Água")puts coffee.ingredients
coffee = Coffee.new(1.5, "Café, Água")puts coffee.ingredients Café, Água
coffee = Coffee.new(1.5, "Café, Água")coffee = MilkDecorator.new coffeeputs coffee.ingredientsputs coffee.price
coffee = Coffee.new(1.5, "Café, Água")coffee = MilkDecorator.new coffeeputs coffee.ingredientsputs coffee.price
Café, Água, Leite
coffee = Coffee.new(1.5, "Café, Água")coffee = MilkDecorator.new coffeeputs coffee.ingredientsputs coffee.price 2.0
class ChocolateDecorator def initialize (coffee)
@coffee = coffee end def price
coffee.price + 1.0 end def ingredients
coffee.ingredients + ", Chocolate" endend
coffee = Coffee.new(1.5, "Café, Água")coffee = ChocolateDecorator.new coffeeputs coffee.ingredients
coffee = Coffee.new(1.5, "Café, Água")coffee = ChocolateDecorator.new coffeeputs coffee.ingredients
Café, Água, Chocolate
coffee = Coffee.new(1.5, "Café, Água")coffee = ChocolateDecorator.new coffeeputs coffee.ingredients
coffee = Coffee.new(1.5, "Café, Água")coffee = ChocolateDecorator.new coffee
coffee = Coffee.new(1.5, "Café, Água")coffee = ChocolateDecorator.new coffeecoffee = MilkDecorator.new coffee
coffee = Coffee.new(1.5, "Café, Água")coffee = ChocolateDecorator.new coffeecoffee = MilkDecorator.new coffeeputs coffee.ingredients
Café, Água, Chocolate, Leite
Singleton
Garantir que só haja uma instancia de determinado objeto
@count
@count@@count
class CountTester @@count = 0 def initialize
@count = 0 end def self.increment
@@count++ end def increment
@count++ endend
class CountTester @@count = 0 def initialize
@count = 0 end def self.increment
@@count++ end def increment
@count++ endend
Variável Global
class CountTester @@count = 0 def initialize
@count = 0 end def self.increment
@@count++ end def increment
@count++ endend
Variável Local
class CountTester @@count = 0 def initialize
@count = 0 end def self.increment
@@count++ end def increment
@count++ endend
Metódo Estático
class CountTester @@count = 0 def initialize
@count = 0 end def self.increment
@@count++ end def increment
@count++ endend
Metódo de Instância
class CountTester COUNT = 0end
Variável Global
class Logger @@instance = Logger.new
def self.instance@@instance
endend
logger1 = Logger.instancelogger2 = Logger.instance
logger = Logger.new
class Logger @@instance = Logger.new
def self.instance@@instance
end
end
class Logger @@instance = Logger.new
def self.instance@@instance
end
private_class_method :newend
require "singleton"class Logger
include Singletonend
logger = Logger.new
logger = Logger.new
private method ‘new’ called for Logger::Class
logger = Logger.instance
Obrigado
http://github.com/fellix
@rs_felix
http://www.crafters.com.br @crafterstudio
http://blog.rollingwithcode.com