Patrones de diseño (en Ruby) — RubyConf Uruguay 2010

Post on 05-Jul-2015

1.392 views 0 download

Transcript of Patrones de diseño (en Ruby) — RubyConf Uruguay 2010

Photo by lucynieto http://www.flickr.com/photos/lucynieto/2299831355/

Patrones de diseño(en ruby)

Ignacio (Nacho) Facello

@nachof

nacho@nucleartesuji.com

Photo by lucynieto http://www.flickr.com/photos/lucynieto/2299831355/

Patrones de diseño(en ruby)

Ignacio (Nacho) Facello

@nachof

nacho@nucleartesuji.com

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.

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

Todo lo que sé de patrones lo aprendí

jugando go

Joseki

El mismo joseki...

… en diferentes contextos.

Cuando no sirven...

● Que algo sea un patrón no quiere decir que sea adecuado.

● El contexto lo es todo.

Lo importante: elegir la herramienta adecuada

Photo by jannem http://www.flickr.com/photos/jannem/3312116875/

Algunos ejemplos de patrones en Ruby

Singleton

require 'singleton' # From stdlib

class SingletonExample include Singletonend

one = SingletonExample.instancetwo = SingletonExample.instance

puts one == two #=> true

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 endend

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 endend

car = Car.newSpeedWarner.new(car, 70)FuelWarner.new(car, 10)

car.run

Staterequire '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" endend

class Go < StatePattern::State def next sleep 2 transition_to (Caution) end

def color "Green" endend

class Caution < StatePattern::State def next sleep 1 transition_to (Stop) end

def color "Amber" endend

class TrafficSemaphore include StatePattern set_initial_state Stopend

semaphore = TrafficSemaphore.new

loop do puts semaphore.color semaphore.nextend

Adapterrequire 'forwardable'

class LegacyClassA def some_method(a, b) puts "#{a}, #{b} (old A)" endend

class LegacyClassB def do_something(b, a) puts "#{a}, #{b} (old B)" endend

class AdapterA extend Forwardable

def initialize(adaptee) @adaptee = adaptee end

def_delegator :@adaptee, :some_method, :actionend

class AdapterB def initialize(adaptee) @adaptee = adaptee end

def action(a, b) @adaptee.do_something(b, a) endend

adapted_a = AdapterA.new(LegacyClassA.new)adapted_b = AdapterB.new(LegacyClassB.new)

[adapted_a, adapted_b].each { |adapted| adapted.action("Hello", "World") }

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 endend

list = [1,2,3,4]iterator = ArrayIterator.new(list)

while (!iterator.over?) do puts iterator.current iterator.nextend

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 endend

list = [1,2,3,4]iterator = ArrayIterator.new(list)

while (!iterator.over?) do puts iterator.current iterator.nextend

Mucho más sencillo...

list = [1,2,3,4]

list.each do |item| puts itemend

Otras formas de iterar

list = [1,2,3,4]

list.each do |item| puts itemend

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

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" endend

class DefaultStrategy def survive puts "Hide from tigers" endend

class AggressiveStrategy def survive puts "Grab spear, hunt tigers" endend

class ConfusedStrategy def survive puts "Grab tiger, hunt spears" endend

og = Caveman.newog.normal_dayog.strategy = AggressiveStrategy.newog.normal_day

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" endend

og = Caveman.newog.normal_dayog.strategy = lambda { puts "Grab spear, hunt tiger" }og.normal_day

Algunos usos

● Rails: Model-View-Controller● ActiveRecord::Observer: Observer● Array#sort: Strategy

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/)

Gracias :-)