ruby idiom for attribute definition?

C

Corey

Hey!

What do you guys do when you have a class with lots of simple attributes?

Do you use attr_reader, and attr_writer shortcuts with each of the attributes?
Or do you use a singe method (say, object.set('attr', 'val') for all of them?
Or do you manually create each of the reader/writer methods?

Also, how many attributes does it take to be considered "alot"?

( I've got a really small class going, with about 9 or 10 simple attributes -
each of them readable and writeable. )


Thanks!

Beers,

Corey
 
D

Dave Thomas

( I've got a really small class going, with about 9 or 10 simple
attributes -
each of them readable and writeable. )

Have you considered using Struct for this? It creates the initialize
method for you, as well as all the attribute accessors. You can extend
the class it creates, but you have to access the attributes via
accessors, and not via instance variables

irb(main):001:0> Thing = Struct.new:)name, :age)
=> Thing
irb(main):002:0> class Thing
irb(main):003:1> def desc
irb(main):004:2> "Person #{self.name} is #{self.age} years old"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> t = Thing.new("Walter", 22)
=> #<struct Thing name="Walter", age=22>
irb(main):008:0> t.desc
=> "Person Walter is 22 years old"

You could also say

class Thing < Struct.new(...)


Cheers

Dave
 
N

Neil Stevens

Hey!

What do you guys do when you have a class with lots of simple attributes?

Do you use attr_reader, and attr_writer shortcuts with each of the attributes?
Or do you use a singe method (say, object.set('attr', 'val') for all of them?
Or do you manually create each of the reader/writer methods?

Try using attr_accessor, which makes an attribute readable and writable.
 
J

jim

* Dave Thomas said:
You could also say

class Thing < Struct.new(...)

This got me thinking, so I tried the following:

class A < Struct.new:)a)
def peek
puts @a
end
end

A.new.peek

Turns out that this does not work since @a is NOT defined.
Problem is, I can't see any instance variables defined
inside a struct. Where does it keep the data?
 
D

Dave Thomas

This got me thinking, so I tried the following:

class A < Struct.new:)a)
def peek
puts @a
end
end

A.new.peek

Turns out that this does not work since @a is NOT defined.
Problem is, I can't see any instance variables defined
inside a struct. Where does it keep the data?

That's why I said you have to use the accessor methods. Structs cheat.

Matz: we discussed this before, but it would be really nice if Struct
set up the IVs too: that way this idiom would be more generally useful.


Cheers

Dave
 
C

Corey

Have you considered using Struct for this? It creates the initialize
method for you, as well as all the attribute accessors.
class Thing < Struct.new(...)

I can see the usefullness in this, thanks for the heads-up - but I
can see that if I had a lot of attributes, it would seem somewhat
awkward:

class Thing < Struct.new :)attr1, :attr2, :attr3, :attr4, :attr5,
:attr6,:attr7, :attr8, :attr9, :attr10 )
# do stuff
end

Besides, I just get a weird feeling that this Struct approac is kinda
odd - to tricky or something - as I'm learning ruby, I'm trying to stick
with the most common idioms and oop practices.

I guess it's just a matter of personal aesthetics? :

class Thing
attr_accessor :attr1, :attr2, :attr3, :attr4, :attr5,
:attr6,:attr7, :attr8, :attr9, :attr10
# do stuff
end


Thanks,

Corey
 
C

Corey

Try using attr_accessor, which makes an attribute readable and writable.

Cool - I hadn't run into that in the Pickaxe yet; I think I like it a little
better that the Struct suggestion ( thanks Dave ) for my particular
purposes in this case.

Thanks for the clues!

Cheers,

Corey
 
J

James Edward Gray II

Cool - I hadn't run into that in the Pickaxe yet; I think I like it a
little
better that the Struct suggestion ( thanks Dave ) for my particular
purposes in this case.

For what it's worth, I think the Struct is better OO design. I'm
firmly in the "Ask for help, not information." camp of OO programmers,
so I try to avoid "getters/setters" whenever possible.

A Struct on the other hand is just a record holder to my mind, so I
have much less problem with using it in a case like this.

Not trying to start a Holy War here, just tossing in my two cents.

James Edward Gray II
 
D

Dave Thomas

For what it's worth, I think the Struct is better OO design. I'm
firmly in the "Ask for help, not information." camp of OO programmers,
so I try to avoid "getters/setters" whenever possible.

A Struct on the other hand is just a record holder to my mind, so I
have much less problem with using it in a case like this.

Exactly - that's what made me suggest it. A class with a large number
of attr_accessors is not really an encapsulated class, so it felt to me
that a Struct would be more appropriate.

I almost suggested using a Hash... :)


Cheers

Dave
 
C

Corey

And from the perspective a new ruby user, I'm in the "ask for help _and_
information" camp - so these explanations are really beneficial, thanks!

Exactly - that's what made me suggest it. A class with a large number
of attr_accessors is not really an encapsulated class, so it felt to me
that a Struct would be more appropriate.

I almost suggested using a Hash... :)

Heh - I actualy started off with a Hash, but I thought that was just my
old perl habits trying break through... <grin>

I'm in the midst of writing another question, which looks to be related
to all this - ( perhaps I should be using a struct or hash after all ) -
hopefully you guys can point me in the right direction.
 
J

jim

* Dave Thomas said:
Exactly - that's what made me suggest it. A class with a large number
of attr_accessors is not really an encapsulated class, so it felt to me
that a Struct would be more appropriate.

Below is an example that speaks to your comment about structs not being
generally useful for inheritance:

S = Struct.new:)a, :b)

class C < S
attr_accessor :c, :d

def do_something_clever
"#{a} : #{b} | #{@c} : #{@d}"
end
end

The problem is that I have to know which IV came from C
and which came from Struct. I suppose that I could use
the getter/setter methods for all IV's, but I thought
that @c was avoiding a function call that 'c' was making,
and I would like to avoid that.

Dave, earlier you suggested that Structs cheat. Well,
I suppose I could try to out cheat the struct with:

class C < S
attr_accessor :a, :b, :c, :d
def ...
end
end

Now I get all the benefits of Struct for :a and :b,
but the problem is that I am violating DRY.
 
H

Hal Fulton

Dave said:
A class with a large number of
attr_accessors is not really an encapsulated class

Now *that* is food for thought. It makes me have second thoughts
about some of my recent code.

Would you be interested in elaborating on this theme?


Hal
 
D

Dave Thomas

Below is an example that speaks to your comment about structs not
being
generally useful for inheritance:

S = Struct.new:)a, :b)

class C < S
attr_accessor :c, :d

def do_something_clever
"#{a} : #{b} | #{@c} : #{@d}"
end
end

The problem is that I have to know which IV came from C
and which came from Struct. I suppose that I could use
the getter/setter methods for all IV's, but I thought
that @c was avoiding a function call that 'c' was making,
and I would like to avoid that.

I'd argue that using the IVs of a parent class is probably not the best
thing to do anyway. It couples implementations, not just interfaces. So
generally I'll use the attributes of superclasses, not IVs. In that
case, the Struct thing is a wash.


Cheers

Dave
 
D

Dave Thomas

Now *that* is food for thought. It makes me have second thoughts
about some of my recent code.

Would you be interested in elaborating on this theme?

A class encapsulates behavior and state. It uses that behavior to
modify that state. That's important, because it means you have a single
place to look when you want to change functionality or fix bugs.

Imaging you have class BankAccount. It provides methods such as
transfer_to(other_account) and so on. It also exposes the current
balance.

First let's imagine it's written like this:

class BankAccount
attr_reader :balance
def transfer_to(other_account)
...
end
end

During acceptance testing, we notice that the balance is off by a penny
at the end of a (test) day. Where do we have to look for the problem?
Well, the only thing that can set the balance is transfer_to() and the
other methods of BankAccount. The problem must be in there somewhere.
It's bounded, and amenable to unit testing.

Now imagine we'd written it as

class BankAccount
attr_accessor :balance
def ...

At the end of the day, we have the same problem, Now where do we look?
Eek! Everywhere. Any code that sets the balance from the outside is a
suspect. We've totally lost the benefits of encapsulation.

For this (and for many other reasons), Ruby doesn't make instance
variables public. Using attr_accessor and attr_writer is often an
appropriate way of circumventing Ruby's shyness. However, these two
functions always cause me to think twice when I use them. "What is the
potential damage that this breach in encapsulation can cause? Is it
worth it?"


Cheers

Dave
 
H

Hal Fulton

[snip excellent discussion]

Very true. And yet sometimes I have found myself wishing for a
Struct that behaved more like a "real" class -- for example,
one that I could reopen and add methods to (accessing predictable
instance variable names).

For my own use, I've created a SuperStruct class. (I dislike the
name, but have used it for a while and can't think of a better one.)

I suppose I'll release it in a day or two and let people decide
whether it is a Useful Tool or just another Dumb Idea.


Hal
 
J

James Edward Gray II

Now *that* is food for thought. It makes me have second thoughts
about some of my recent code.

Would you be interested in elaborating on this theme?

I didn't make the comment, but I'll take a crack at the answer, if
you'll allow it.

Data Abstraction is arguably the most important, and I think most
overlooked feature, of Object Oriented Programming. You want your
classes to be self-contained experts on their field of interest. As
experts, they should be capable of handling the task for their data,
instead of exposing that data so others can handle the tasks for them.

That's the heart of OO Design, in my opinion, though you rarely see
this pure form in "the wild". To me, that's what Design Patterns are
all about, tactics to preserve this encapsulation in Real World
I'm-Going-To-Need-To-Maintain-This Software.

Generally, programmers design classes to hold some related data and do
a few things with it. Then they throw in a bunch of accessors so user
code can see what happened/export/HTMLify or about a million other
uses. We're always "pulling" data out, but The Right Way (My opinion!)
to do objects is to "push" the details into the expert object instead.
(Matz eluded to this in a recent post, though he was quasi-discussing
the web.)

We know public instance data is BAD, right? How does putting a method
over it make it all better? The weak answer is that it provides you
with an option to change how that data is stored in the future. If
that's the case, why give access to it at all? It creates tighter
dependancies with all user code and we all know that's bad because it
complicates maintenance. Go the distance and just don't give out the
data to begin with. Instead, tell users, you just give me what I need
and I'll handle it for you.

Don't read all the data and export it to some format, pass in some kind
of "exporter form" object, the expert can fill out. Don't poll a Clock
object for the current time, ask it to notify you when the time you
desire has been reached. Don't have a Car manage it's location details
through accessors, have the Map object pass in the Car's current
location as an argument to a method that needs it.

"Ask for help, not information."

The above is the reminder I use to test if I'm designing correctly.
There are exceptions, of course, but that's the central focus of OO
Design to me.

Hope that helps and I hope I didn't put too many wrong words in Dave's
or Matz's mouths.

James Edward Gray II
 
J

James Britt

Corey wrote:
...
I can see the usefullness in this, thanks for the heads-up - but I
can see that if I had a lot of attributes, it would seem somewhat
awkward:

class Thing < Struct.new :)attr1, :attr2, :attr3, :attr4, :attr5,
:attr6,:attr7, :attr8, :attr9, :attr10 )
# do stuff
end

Besides, I just get a weird feeling that this Struct approac is kinda
odd - to tricky or something - as I'm learning ruby, I'm trying to stick
with the most common idioms and oop practices.

I suspect that "common idioms and oop practices" are very
language-dependent. The use of external iterator objects are a common
Java idiom, but it doesn't strike me as particularly OO.
I guess it's just a matter of personal aesthetics? :

That's part of it, but if you are new to a language, many of the idioms
may strike you as aestheticly distasteful, yet are indeed quite elegant
within their context.

It may be better to just dive in, try doing things a Ruby Way, and see
if that doesn't alter how you think about OO design and programming,
rather than having particular notions about OOP direct how you use Ruby
(or any other language, for that matter).

James
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: ruby idiom for attribute definition?"

|That's why I said you have to use the accessor methods. Structs cheat.

I don't consider it cheating. Accessing instance variables is sort of
cheating (that's part of the reason for ugly "@").

|Matz: we discussed this before, but it would be really nice if Struct
|set up the IVs too: that way this idiom would be more generally useful.

Hmm, instance variables are internal information. If you don't know
the internal, you should use accessors (self.a this case), even in the
methods. I'm not excited for Structs to have their members in
instance variables.

matz.
 
D

Dave Thomas

Hi,

In message "Re: ruby idiom for attribute definition?"
on Fri, 12 Nov 2004 04:06:39 +0900, Dave Thomas

|That's why I said you have to use the accessor methods. Structs cheat.

I don't consider it cheating. Accessing instance variables is sort of
cheating (that's part of the reason for ugly "@").

Except in structs you still have the instance variables (the state has
to be somewhere). You just can't access them. That's what I meant by
cheating... :)
|Matz: we discussed this before, but it would be really nice if Struct
|set up the IVs too: that way this idiom would be more generally
useful.

Hmm, instance variables are internal information. If you don't know
the internal, you should use accessors (self.a this case), even in the
methods. I'm not excited for Structs to have their members in
instance variables.

Agreed in general (in fact I argued that a couple of posts back). But
the idiom

class A < Struct:)x, :y)
end

could be really useful for classes that want IVs initialized from the
constructor, and which provide accessors for them by default, that I
wonder if we could bend the rules :) I tend to use the construct for
Structs where I want to add just one or two helper methods.


Cheers

Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top