attr_writer (Setter) mit Block-Argument?

21/10/2007 - 22:50 von Jörg W Mittag | Report spam
=begin
Hi,

ich spiele gerade mit ein paar Entwurfsmuster-Beispielen aus dem
Buch "Head First Design Patterns" herum und versuche jeweils die
rubyeskeste Entsprechung für das Java-Beispiel im Buch zu finden.

Dabei bin ich beim Strategie-Muster auf ein Problem gestoßen; es
geht lediglich um ganz simples Verhalten, das zur Laufzeit
gesetzt werden soll. Was also ist die rubyeskeste Möglichkeit,
Verhalten zu setzen? Naja, bei "Verhalten" denken wir natürlich
sofort an Blöcke und bei "setzen" an attr_writer -- nur leider
funktioniert das nicht.

So sieht die Implementierung mit einem Standard-Java-Setter aus:
=end

Foo = Object.new
def Foo.set_bar(bar=nil, &block) @bar = block || bar; end
def Foo.do_bar; @bar.call; end

=begin
Und so wird das ganze benutzt: mit set_bar wird das Verhalten für
die bar-Aktion gesetzt und mit do_bar wird die bar-Aktion
ausgeführt.
=end

begin require 'rubygems'; gem 'rspec'; rescue LoadError; end
begin require 'spec'; rescue LoadError; puts 'Install RSpec'; end

describe Foo, '#set_bar' do

it 'can take a lambda' do
Foo.set_bar lambda { 'Foo' }
Foo.do_bar.should == 'Foo'
end

it 'can take a block' do
Foo.set_bar { 'Foo' }
Foo.do_bar.should == 'Foo'
end

end

=begin
Der Java-Setter ist aber hàßlich, ein attr_writer (bar=) wàre
doch viel schöner, oder?
=end

def Foo.bar=(bar=nil, &block) @bar = block || bar; end

describe Foo, '#bar=' do

it 'can take a lambda' do
Foo.bar = lambda { 'Foo' }
Foo.do_bar.should == 'Foo'
end

it 'can take a block' do
violated "This doesn't work."
#Foo.bar = { 'Foo' } # Parser thinks it's a Hash
#Foo.bar=({ 'Foo' }) # ditto
Foo.do_bar.should == 'Foo'
end

end

=begin
Ich habe mich schon immer gefragt, wie der Ruby-Parser zwischen
einem Block-Literal und einem Hash-Literal zuverlàssig
unterscheiden kann. Ich schàtze, jetzt weiß ich die Antwort: gar
nicht! Oder habe ich eine Möglichkeit übersehen? Eventuell durch
geschickte Klammerung o.à.? (Obwohl das natürlich nicht viel
bringt, denn es würde die nette Lesbarkeit des Konstruktes
zerstören.)

Vielen Dank im voraus,
jwm
=end
 

Lesen sie die antworten

#1 Wolfgang Nádasi-Donner
22/10/2007 - 00:00 | Warnen spam
Jörg W Mittag schrieb:
Oder habe ich eine Möglichkeit übersehen? Eventuell durch
geschickte Klammerung o.à.? (Obwohl das natürlich nicht viel
bringt, denn es würde die nette Lesbarkeit des Konstruktes
zerstören.)



Kurze Antwort - es geht nur über "send", womit die ansprechende Optik zerstört
ist...

irb(main):001:0> class Otto
irb(main):002:1> def x=(val,&blk)
irb(main):003:2> blk.call(val)
irb(main):004:2> end
irb(main):005:1> def y=(&blk)
irb(main):006:2> blk.call
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> o = Otto.new
=> #<Otto:0x30e55f8>
irb(main):010:0> o.send(:x=,5){|p|p*3}
=> 15
irb(main):011:0> o.send(:y=){42}
=> 42

Wolfgang Nádasi-Donner

Ähnliche fragen