[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: Too many default argument values!

rdlugosz.1044583

10/20/2004 1:37:00 PM

> Or slightly simpler:
> DEFAULT_OPTIONS = { ... }
> def my_func( arg1,
... argN, options = { } )
> options = DEFAULT_OPTIONS.merge(options)
>
# ...
> end

Seems like this is all a bit complicated for what you're
doing. I mean - once you do all of this fancy default hash stuff, won't you
still have to go look it up in the docs / source every time to see what options
are available? Why not just set the parameters for things like rounded edges
and fill colors after the fact using method calls on your new object? Granted
you won't be able to do it in just one call to new() as you can with the hash,
but is that really more clear to an outsider (or *you* later on) looking at
your code?

Another option (which I'm betting won't be popular here...)
would be to have a separate RectangleBuilder class that will make Rectangles
for you. You could have multiple methods here that would take reasonable
parameter sets based on their name.

Just a couple of options I wanted
to throw out there.

-Ryan


7 Answers

Robert Klemme

10/20/2004 1:48:00 PM

0


<rdlugosz.1044583@bloglines.com> schrieb im Newsbeitrag
news:1098279415.3771384755.15079.sendItem@bloglines.com...
> > Or slightly simpler:
> > DEFAULT_OPTIONS = { ... }
> > def my_func( arg1,
> .. argN, options = { } )
> > options = DEFAULT_OPTIONS.merge(options)
> >
> # ...
> > end
>
> Seems like this is all a bit complicated for what you're
> doing. I mean - once you do all of this fancy default hash stuff, won't
you
> still have to go look it up in the docs / source every time to see what
options
> are available? Why not just set the parameters for things like rounded
edges
> and fill colors after the fact using method calls on your new object?
Granted
> you won't be able to do it in just one call to new() as you can with the
hash,
> but is that really more clear to an outsider (or *you* later on) looking
at
> your code?

Definitely! I just felt that the OP wanted to do it with one method
invocation, but of course the approach to set properties individually is
much more flexible - especially if inheritance comes into play.

> Another option (which I'm betting won't be popular here...)
> would be to have a separate RectangleBuilder class that will make
Rectangles
> for you. You could have multiple methods here that would take
reasonable
> parameter sets based on their name.
>
> Just a couple of options I wanted
> to throw out there.

Good that you pointed that out.

robert

Eivind Eklund

10/20/2004 4:12:00 PM

0

On Wed, 20 Oct 2004 22:36:59 +0900, rdlugosz.1044583@bloglines.com
<rdlugosz.1044583@bloglines.com> wrote:
> Another option (which I'm betting won't be popular here...)
> would be to have a separate RectangleBuilder class that will make Rectangles
> for you. You could have multiple methods here that would take reasonable
> parameter sets based on their name.

I tend to like using a set of factory methods.

I would, however, not create a separate RectangleBuilder class unless
I need configurable factories. Instead, I'd exploit the metaclass
system of Ruby, and just do
class Rectangle
def self.new_rounded(...)
end
# etc
end

I'll also in passing note that when working with (very) similar
problems, I've occasionally found it useful to make the styles
parameter an object in its own right, to have a separate inheritance
hierarchy with the possibiltity to vary aspects of styles that end up
being calculated from each other etc.

Eivind.
--
Hazzle free packages for Ruby?
RPA is available from http://www.rubyar...


Gavin Sinclair

10/20/2004 5:02:00 PM

0

On Wednesday, October 20, 2004, 11:36:59 PM, rdlugosz wrote:

> Another option (which I'm betting won't be popular here...)
> would be to have a separate RectangleBuilder class that will make Rectangles
> for you. You could have multiple methods here that would take reasonable
> parameter sets based on their name.

The last serious Ruby project I worked on used a similar approach. It
was constructing something serious. Can't remember what; let's use
Rectangles.

class Rectangle
attr_accessor :x1, y1, ...
...
end

class Rectangle::Generator
...
end

class Rectangle::Generator::Parameters
attr_accessor :x, :y, :rx, ...
end

Usage was something like:

rect = Rectangle.generate { |p|
p.x = 5
p.y = 10
...
}

The Rectange::Generator is justified if there is significant logic
concerned with generation - i.e. not just setting attributes. The
Rectangle::Parameters provides an opportunity to clearly document the
parameters used for generation.

The upside of this is that the Rectangle class was short and sweet;
all the behind-the-scenes logic was nicely tucked away.

The downside is that a single mode of generation might be entrenched.
As Eivund has pointed out, different "factory methods" (or "generators",
or "constructors") can conveniently do different things.

The above approach is obviously overkill for a simple rectangle, but
for really complex objects it's a pattern I'm sure I'll use again.

Cheers,
Gavin



ggarramuno

10/22/2004 8:29:00 PM

0

Gavin Sinclair <gsinclair@soyabean.com.au> wrote in message news:<701387602747.20041021030313@soyabean.com.au>...
> On Wednesday, October 20, 2004, 11:36:59 PM, rdlugosz wrote:
>
> > Another option (which I'm betting won't be popular here...)
> > would be to have a separate RectangleBuilder class that will make Rectangles
> > for you. You could have multiple methods here that would take reasonable
> > parameter sets based on their name.
>
> The last serious Ruby project I worked on used a similar approach. It
> was constructing something serious. Can't remember what; let's use
> Rectangles.
>

Gavin's approach is the one I like best.
Putting the logic of creation in another class is also a good idea
(that way you could enforce what parameters need to be defined or do
some more esoteric things if need arises).
Here's more fleshed out code, which I did just to think about it a
tad:

class Rectangle
def initialize
@p = Parameters.new
yield @p if block_given?
end

def method_missing(*args)
@p.send(*args)
end
end

class Rectangle::Parameters
attr_accessor :x, :y, :w, :h

def initialize
@x = @y = @w = @h = 0
end
end


a = Rectangle.new { |p|
p.x = 20
p.y = 10
p.w = 5
p.h = 8
}

b = Rectangle.new

puts "Custom rect: #{a.x} #{a.y}"
puts "Default rect: #{b.x} #{b.y}"


There's just two minor issues with such code. One is that each
access/setting of a value is now going thru an added level of
indirection, so you do pay a small speed penalty for doing it this
way. Having a factory method and a flat rectangle class instead
avoids this issue.

Another minor issue is that in case of a typo ( say, above, you use
a.width = 20, instead of a.w = 20, the NoMethodError may be a tad
confusing as it exposes the innards of your code in reporting errors
about the presumably hidden parameter class ).

However, where this approach is of not much use is on some method
invocation that requires a long list of optional parameters. Creating
a new object and filling its data just for calling an instance's
method still feels too much C like and awkward.
That's still one thing where named parameters built into the language
seem like a good idea and one thing that I do miss from python.

Tim Hunter

10/22/2004 11:23:00 PM

0

GGarramuno wrote:
>
> However, where this approach is of not much use is on some method
> invocation that requires a long list of optional parameters. Creating
> a new object and filling its data just for calling an instance's
> method still feels too much C like and awkward.
> That's still one thing where named parameters built into the language
> seem like a good idea and one thing that I do miss from python.

I actually used a very similar approach in RMagick, where many of the
methods have a very long list of optional parameters.

I had just about decided to split the `rect' method into two methods, one
for square-cornered rectangles and one for round-cornered rectangles, but
then I saw that I was going to have to do something with _this_ monster, a
sibling method to `rect':

def text(text, x, y, dx, dy, rotate, styles)
# blah, blah blah
end

Here the last 6 arguments are optional! Since I want to use a consistent
style among all these methods, I need to choose a solution for `text' and
use it for `rect' as well. The simplest thing to do would be to (as other
people have suggested) put the optional arguments in with the `style' hash,
but I'm reluctant to mix both style and non-style arguments like that.

Back to the drawing board. I'm going to review all the suggestions in this
thread for illumination and advice.

T. Onoma

10/23/2004 1:50:00 PM

0

On Friday 22 October 2004 07:24 pm, Tim Hunter wrote:
| def text(text, x, y, dx, dy, rotate, styles)
| # blah, blah blah
| end
|
| Here the last 6 arguments are optional! Since I want to use a consistent
| style among all these methods, I need to choose a solution for `text' and
| use it for `rect' as well. The simplest thing to do would be to (as other
| people have suggested) put the optional arguments in with the `style' hash,
| but I'm reluctant to mix both style and non-style arguments like that.
|
| Back to the drawing board. I'm going to review all the suggestions in this
| thread for illumination and advice.

Recall that I was having s similar problem. I took a page out of Rich Kilmer's
work and created a couple self returning methods, so applied to your case
something like:

Rectangle.new(x,y).d(dx, dy).rotate(r).styles(s)

Such that

def d(x,y)
@dx = x
@dy = y
self
end

# etc.

T.


Tim Hunter

10/23/2004 2:10:00 PM

0

trans. (T. Onoma) wrote:

> Recall that I was having s similar problem. I took a page out of Rich
> Kilmer's work and created a couple self returning methods, so applied to
> your case something like:
>
> Rectangle.new(x,y).d(dx, dy).rotate(r).styles(s)

Hmmm...I like this. I like this a lot. Easy to read, easy to code.

Of course Rich's work is always inspiring to me.