Ruby idiom for attribute definition?

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

···

--

Misery is the river of the world.
Everybody row.
  - Tom Waits

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

···

On Nov 10, 2004, at 22:27, Corey wrote:

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

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

···

On Thu, 11 Nov 2004 13:27:42 +0900, Corey wrote:

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?

--
Neil Stevens - neil@hakubi.us
"The world is a dangerous place to live; not because of the people who
are evil, but because of the people who don't do anything about it."
                                                 -- Albert Einstein(?)

* Dave Thomas <dave@pragprog.com> [2004-11-11 13:40:43 +0900]:

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?

···

--
Jim Freeze

> ( 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.

<snip>

   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

···

On Wednesday 10 November 2004 09:40 pm, Dave Thomas wrote:

On Nov 10, 2004, at 22:27, Corey wrote:

--

"Good judgment comes from experience.
Experience comes from bad judgment."
  - Jim Horning

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

<snip>

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

···

On Wednesday 10 November 2004 10:53 pm, Neil Stevens wrote:

On Thu, 11 Nov 2004 13:27:42 +0900, Corey wrote:

--

"Never believe anything until it is officially denied."
  - Claud Cockburn

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

···

On Nov 11, 2004, at 12:52, jim@freeze.org wrote:

* Dave Thomas <dave@pragprog.com> [2004-11-11 13:40:43 +0900]:

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?

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

···

On Nov 11, 2004, at 1:18 PM, Corey wrote:

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.

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

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... :slight_smile:

Cheers

Dave

···

On Nov 11, 2004, at 13:29, James Edward Gray II wrote:

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.

Hi,

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.

···

In message "Re: ruby idiom for attribute definition?" on Fri, 12 Nov 2004 04:06:39 +0900, Dave Thomas <dave@pragprog.com> writes:

> 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.
>

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!

> 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... :slight_smile:

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.

···

On Thursday 11 November 2004 12:46 pm, Dave Thomas wrote:

On Nov 11, 2004, at 13:29, James Edward Gray II wrote:

--

"To a mind that is still, the whole universe surrenders."
  - Chuang Tzu

* Dave Thomas <dave@pragprog.com> [2004-11-12 04:46:04 +0900]:

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.

···

--
Jim Freeze

Dave Thomas wrote:

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

Hi,

>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... :slight_smile:

>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 :slight_smile: I tend to use the construct for Structs where I want to add just one or two helper methods.

Cheers

Dave

···

On Nov 11, 2004, at 19:43, Yukihiro Matsumoto wrote:

In message "Re: ruby idiom for attribute definition?" > on Fri, 12 Nov 2004 04:06:39 +0900, Dave Thomas > <dave@pragprog.com> writes:

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

···

On Nov 11, 2004, at 14:21, jim@freeze.org wrote:

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.

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

···

On Nov 11, 2004, at 14:26, Hal Fulton wrote:

Dave Thomas wrote:

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?

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

···

On Nov 11, 2004, at 2:26 PM, Hal Fulton wrote:

Dave Thomas wrote:

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?

Dave Thomas wrote:

Would you be interested in elaborating on this theme?

[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

···

On Nov 11, 2004, at 14:26, Hal Fulton wrote: