TeamStation AI System Report LATAM IT Salaries 2024
Patrones de diseño (en Ruby) — RubyConf Uruguay 2010
1. Photo by lucynieto http://www.flickr.com/photos/lucynieto/2299831355/
Patrones de diseño
(en ruby)
Ignacio (Nacho) Facello
@nachof
nacho@nucleartesuji.com
2.
3. Photo by lucynieto http://www.flickr.com/photos/lucynieto/2299831355/
Patrones de diseño
(en ruby)
Ignacio (Nacho) Facello
@nachof
nacho@nucleartesuji.com
4. Qué son patrones de diseño?
● Un patrón de diseño es una solución general
reutilizable a un problema de común ocurrencia
en el diseño de software (Wikipedia)
● Concepto originalmente usado en arquitectura
(Christopher Alexander)
● Popularizado en el libro Design Patterns:
Elements of Reusable Object-Oriented
Software, popularmente conocido como GoF
(Gang of Four), de Erich Gamma, Richard
Helm, Ralph Johnson, y John Vlissides.
5. Para qué sirven?
● Conocer una solución para un problema dado
es útil cuando uno se enfrenta a ese problema.
● Nos dan un lenguaje común.
● Suelen resultar en un buen diseño
6. Todo lo que sé de
patrones lo aprendí
jugando go
13. Singleton
require 'singleton' # From stdlib
class SingletonExample
include Singleton
end
one = SingletonExample.instance
two = SingletonExample.instance
puts one == two #=> true
14. Observer
require 'observer' # From stdlib
class Car
include Observable
def initialize
@fuel = 100
@speed = 0
end
def run
while @fuel > 0 do
@speed += 1
@fuel -= 1
changed
notify_observers (@speed,
@fuel)
end
end
end
class SpeedWarner
def initialize(car,
speed_limit)
@limit = speed_limit
car.add_observer(self)
end
def update(speed, fuel)
puts "Too fast!" if speed >
@limit
end
end
car = Car.new
SpeedWarner.new(car, 70)
FuelWarner.new(car, 10)
car.run
15. State
require 'state_pattern' # Gem by
@dcadenas
# http://github.com/dcadenas/state_pattern
class Stop < StatePattern::State
def next
sleep 3
transition_to (Go)
end
def color
"Red"
end
end
class Go < StatePattern::State
def next
sleep 2
transition_to (Caution)
end
def color
"Green"
end
end
class Caution < StatePattern::State
def next
sleep 1
transition_to (Stop)
end
def color
"Amber"
end
end
class TrafficSemaphore
include StatePattern
set_initial_state Stop
end
semaphore = TrafficSemaphore.new
loop do
puts semaphore.color
semaphore.next
end
16. Adapter
require 'forwardable'
class LegacyClassA
def some_method(a, b)
puts "#{a}, #{b} (old A)"
end
end
class LegacyClassB
def do_something(b, a)
puts "#{a}, #{b} (old B)"
end
end
class AdapterA
extend Forwardable
def initialize(adaptee)
@adaptee = adaptee
end
def_delegator :@adaptee,
:some_method, :action
end
class AdapterB
def initialize(adaptee)
@adaptee = adaptee
end
def action(a, b)
@adaptee.do_something(b, a)
end
end
adapted_a =
AdapterA.new(LegacyClassA.new)
adapted_b =
AdapterB.new(LegacyClassB.new)
[adapted_a, adapted_b].each { |
adapted| adapted.action("Hello",
"World") }
17. Iterator
class ArrayIterator
def initialize(array)
@array = array
@index = 0
end
def first
@index = 0
end
def next
@index += 1
end
def current
@array [@index]
end
def over?
@index >= @array.size
end
end
list = [1,2,3,4]
iterator = ArrayIterator.new(list)
while (!iterator.over?) do
puts iterator.current
iterator.next
end
18. Iterator
class ArrayIterator
def initialize(array)
@array = array
@index = 0
end
def first
@index = 0
end
def next
@index += 1
end
def current
@array [@index]
end
def over?
@index >= @array.size
end
end
list = [1,2,3,4]
iterator = ArrayIterator.new(list)
while (!iterator.over?) do
puts iterator.current
iterator.next
end
20. Otras formas de iterar
list = [1,2,3,4]
list.each do |item|
puts item
end
list.map { |item| item * 2 } #=> [3,4,5,6]
list.any? { |item| item == 2 } #=> true
list.select { |item| item > 2 } #=> [3,4]
list.detect { |item| item > 2 } #=> 3
21. Strategy
require 'forwardable'
class Caveman
extend Forwardable
attr_accessor :strategy
def initialize(strategy =
DefaultStrategy.new)
@strategy = strategy
end
def_delegator :@strategy, :survive
def normal_day
wake_up
survive
go_to_sleep
end
def wake_up
puts "Waking up"
end
def go_to_sleep
puts "Sleep now"
end
end
class DefaultStrategy
def survive
puts "Hide from tigers"
end
end
class AggressiveStrategy
def survive
puts "Grab spear, hunt tigers"
end
end
class ConfusedStrategy
def survive
puts "Grab tiger, hunt spears"
end
end
og = Caveman.new
og.normal_day
og.strategy = AggressiveStrategy.new
og.normal_day
22. Strategy (con lambdas)
class Caveman
attr_accessor :strategy
def initialize(strategy = lambda { puts "Hide from tiger" })
@strategy = strategy
end
def normal_day
wake_up
survive
go_to_sleep
end
def survive
@strategy.call
end
def wake_up
puts "Waking up"
end
def go_to_sleep
puts "Sleep now"
end
end
og = Caveman.new
og.normal_day
og.strategy = lambda { puts "Grab spear, hunt tiger" }
og.normal_day
24. Algunos comentarios finales
● Stdlib y gemas son útiles
● Memorizarse los patrones y usarlos a cada
oportunidad no ayuda.
● Entender sus consecuencias, y su porqué, sí
sirve.
● “There may be a dozen different ways to
implement a [given pattern] - it doesn't have to
be done exactly like the GoF book or some web
page.” Paul Wheaton
(http://www.javaranch.com/patterns/)
Solución conocida a problema dado
Lenguaje común, ejemplo observer
“Mal” ejemplo: goto, o alguna otra cosa
Buen diseño, siempre y cuando estemos hablando del contexto adecuado
Va a ser una metáfora visual
Secuencia de jugadas con un resultado parejo para ambos jugadores
En este caso, blanco esquina, negro afuera
Izquierda: negro trabaja bien
Derecha: la pared de negro se enfrenta a blanco que lo neutraliza
Lo más importante: el contexto.
Es decir, elegir la herramienta adecuada para el problema actual es imprescindible
Uno de los patrones más odiados.
Tiene algunos casos de uso
El ejemplo: porque el soporte está en stdlib — se encarga de temas de concurrencia y múltiples threads
Permite que un objeto notifique a otros sobre cambios en su estado
También soporte en stdlib
Requerir &apos;observer&apos;
En la clase observable, requerir Observable
El método add_observer (en el initializer de los observers) agrega los observers al observable
El método notify_observers en el observable llama al update de los observers, con los mismos argumentos.
Pero sólo si antes llamamos a changed para avisarle
Permite a un objeto cambiar su comportamiento de acuerdo a su estado interno (sin cambiar la interfaz)
Hay muchas librerías que ayudan con State
Elegí esta porque me parece clara
Hasta le robé el ejemplo de código del readme
Cada estado hereda de StatePattern::State
El objeto incluye StatePattern
Y define el estado inicial
transition_to cambia el objeto estado
Los métodos color y next son implícitamente delegados al objeto estado
Hablando de delegar, hay varios patrones que usan delegación, no sólo State.
Adapter, por ejemplo.
Nos permite adaptar la interfaz de varias clases incompatibles a una misma interfaz que podemos usar.
Para eso, es útil Forwardable
En el caso de AdapterA, extiende Forwardable, y luego usa def_delegator.
En el caso de AdapterB, no se puede, porque el orden de los parámetros es diferente
Iterator nos permite recorrer una lista de objetos, como por ejemplo un array, para trabajar con ellos
Implementación casi directa del GoF
Para alguien que venga de Java, va a ser algo bastante conocido
Pero en ese caso estás haciendo Java, no Ruby
En Ruby hay formas más expresivas de lograr lo mismo
Tenemos each para iterar sobre elementos
Cuando iteramos con diferentes objetivos, es más expresivo iterar con métodos que expliquen qué es lo que queremos lograr
Todos estos métodos están definidos en Enumerable
Si tuviéramos otra estructura sobre la que iterar, con sólo implementar each e incluír el módulo Enumerable
Ayer Aaron Patterson mostró un par de implementaciones para un árbol
Permite cambiar la forma en la que un objeto se comporta
Ejemplo medio clásico, con distintos objetos para cada Strategy
Otra opción, tal vez más ruby, sería incluír módulos para cada Strategy diferente
Me gusta más esta manera, parece más ruby
Hay muchos usos de patrones que usamos todos los días, muchos que tenemos tan integrados que casi no nos damos cuenta
Estos son sólo algunos ejemplos, hay muchos más si uno quiere buscar