[OO design] Objects VS Datastructures

Those of you who’ve read [ruby-talk:70422] ( [OO interface design]
Pass-by reference VS encapsulation ?) know I’m designing a lightweight
personal addressbook editor (and using it as a testcase to practice my
design/pattern skills).

From the very start I’ve been feeling uneasy about my setup: I have an
Addressbook class that stores Contact instances which store attributes
(name, birthdate, phoneNr, …). I chose this setup because I wanted (at
the lowest level) an interface that could be used by all clients and
would guarantee data integrity and consistence (the Addressbook prevents
the addition of a contact if an similarly named contact already exists
and the Contact class has writers that throw exceptions when the data
does not comply with the requirements).

The problem I see in this setup is that my ‘Objects’ are very passive;
they are not much more than datastructures with an attitude. Their whole
interface consists of setters and getters. I’ve been wondering if this
is still OO ? Shouldn’t I be sending ‘messages’ instead of “taking some
’Object’, pulling out some information to shove it somewhere else” ?

What are your thoughts on this matter ? Am I doing procedural
programming in a OO language or is this an unavoidable consequence of
the addressbook problem I am trying to solve ?

Corollary: It has also occurred to me that Contact#setName(aString)
where Contact checks if the name satisfies certain
conditions might not be the best setup since a ‘name’ is not
string; it is much narrower than that (in my
implementation). Maybe I’d better create a Name class that
represents a ‘name’ object and enforces it’s integrity.
Doing that for all Contact’s attributes would reduce it’s
responsibilities to the point where it would barely differ
from a Hash, thus exacerbating the question above.

Looking forward to your responses.

– Simon

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.

                                                -- Blair Houghton

Hi –

The problem I see in this setup is that my ‘Objects’ are very passive;
they are not much more than datastructures with an attitude. Their whole
interface consists of setters and getters. I’ve been wondering if this
is still OO ? Shouldn’t I be sending ‘messages’ instead of “taking some
‘Object’, pulling out some information to shove it somewhere else” ?

What are your thoughts on this matter ? Am I doing procedural
programming in a OO language or is this an unavoidable consequence of
the addressbook problem I am trying to solve ?

Corollary: It has also occurred to me that Contact#setName(aString)
where Contact checks if the name satisfies certain
conditions might not be the best setup since a ‘name’ is not
string; it is much narrower than that (in my
implementation). Maybe I’d better create a Name class that
represents a ‘name’ object and enforces it’s integrity.
Doing that for all Contact’s attributes would reduce it’s
responsibilities to the point where it would barely differ
from a Hash, thus exacerbating the question above.

Rather than Contact#setName (or even Contact#set_name :slight_smile: you’d
probably be better off leveraging Ruby’s idiomatic set/get mechanism:

Contact#name # getter
Contact#name= # setter

(which you can do easily with the attr_* family of shortcuts, or by
hand if you need more complexity).

Technically this is no more or less of a “message”-based way than what
you’re doing now (since all method calls in Ruby are based on the
message-sending model), but it’s a better fit with the language and
might start to give you a stronger sense of the Contact objects (and
others) as more than data structures.

David

···

On Wed, 14 May 2003, Simon Vandemoortele wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Simon Vandemoortele wrote:

Corollary: It has also occurred to me that Contact#setName(aString)
where Contact checks if the name satisfies certain
conditions might not be the best setup since a ‘name’ is not
string; it is much narrower than that (in my
implementation). Maybe I’d better create a Name class that
represents a ‘name’ object and enforces it’s integrity.
Doing that for all Contact’s attributes would reduce it’s
responsibilities to the point where it would barely differ
from a Hash, thus exacerbating the question above.

Simon:

Please take this the right way, but… it seems to me that you’re
writing Java, or C++, and not Ruby. Although an interesting exercise,
you’re not really teaching yourself the philosophy of Ruby programming.

Look at the problem the other way around.

Here’s an exercise I go through whenever I design a Ruby program. I
start by looking at the dumbest, simplest implementation, the one that
would never work. For example, for the contact example, I might write:

Contact = Hash
c = Contact.new
c[:name] = "Dave Thomas"
c[:tel]  = "555 1212"

Then I’d say to myself: that’s too simple, it could never work. Then I
ask myself why. And I make myself answer honestly. And often I can’t
come up with a good answer, so I stick with the simple, dumb design, and
everything just works.

Sometimes, though, I come up with objections. In this particular case,
my main objection is that I don’t like all the brackets everywhere. So I
might switch from using a Hash to using OpenStruct:

  Contact = OpenStruct
   c = Contact.new
   c.name = "Dave Thomas"
   c.tel  = "555 1212"

Hmm - feels pretty good. I might go with this.

At some point I might discover I’d like to bundle some additional
behavior in the class. That’s easy: at that point, and not before, I’d
change my class definition

  class Contact < OpenStruct
     def dial_phone
       Dialer.new.dial(self.tel)
     end
  end

Pretty easy - nothing else changes.

After a while, I might decide that basing my class on OpenStruct isn’t
optimal when marshaling, so I decide to implement the accessors myself
instead. I change the class definition again:

class Contact
  attr_accessor :name, :tel
   ...

This happens to me all the time: I start with one implementation (in
this case simply using OpenStruct) and evolve my code to something
totally different (in this case totally away from OpenStruct). This is
one of the things I love about Ruby: it gives me the flexibility to
explore in this way at the same time that I’m writing useful code.

Just for a while, forget about classes, typing, and “good design”.
Totally. Just code up the simplest code that could work (and surprise
yourself at just how simple that code can be).

Sorry to preach :slight_smile:

Cheers

Dave

What are your thoughts on this matter ? Am I doing procedural
programming in a OO language or is this an unavoidable consequence of
the addressbook problem I am trying to solve ?

Corollary: It has also occurred to me that Contact#setName(aString)
where Contact checks if the name satisfies certain
conditions might not be the best setup since a ‘name’ is not
string; it is much narrower than that (in my
implementation). Maybe I’d better create a Name class that
represents a ‘name’ object and enforces it’s integrity.
Doing that for all Contact’s attributes would reduce it’s
responsibilities to the point where it would barely differ
from a Hash, thus exacerbating the question above.

Is not a contact in a book merely a hash with a perhaps limited set of
keys?

It’s always seemed to me that a contact is an extremely simple thing in
concept, but it contains some datatypes with some very specific
constraints. I think that you’re on the right track with the ‘Name’
class…

Ari

in message news:Zcdwa.1038$1u5.25@afrodite.telenet-ops.be…

The problem I see in this setup is that my ‘Objects’ are very passive;
they are not much more than datastructures with an attitude. Their whole
interface consists of setters and getters. I’ve been wondering if this
is still OO ? Shouldn’t I be sending ‘messages’ instead of “taking some
‘Object’, pulling out some information to shove it somewhere else” ?

That is a good observation. Many OO designs fail partially because designers
say - wow this is an thing in the problem domain. It has name so it must be
an object. Thus we create a class for that object and figure out how to
relate it to other objects.
When designing properly, it is often discovered that non-obvious entities
are the real objects in the problem domain.

For example the address book could have an object called
ContactUpdateManager. This manager would server a single client and
coordinate its efforts with other ContactUpdateManager objects, or possibly
with other UpdateManagers that updates non-contact information. Note: this
is just an example. I’m not sure it would actually be smart to create
UpdateManagers - it’s just a different perspective.

To the question about datastructures:
In a functional programming language such as OCaml, you would typically have
datastructures instead of objects. Instead you have a family of functions
that operate on these datastructures. If you want to map this view onto
objects again, you get a kind of manager style object that manages the
datastructure, not a single data entity. I guess this is why functional
programming is relatively successfull (not popular, just successful).

When I first learned Ruby, I wrote a pet text adventure program. Ruby was
very good at this. But I didn’t create a single object of a class I had
designed. I used lots of arrays and hashes. It would have been death by
objects to do the same with a class for every thing I made. When I
eventually made it socket enabled so I could connect via telnet, I realized
I needed to have multiple instances. Thus I wrapped all the code in one
large Adventure class and made an instance for each connection. Had I
created multiple players in the same game, I would problably have had Player
objects and Game objects. But the zillions of commands, verbs, rooms,
relations between rooms etc. was much better handled by datastructures.

So I say be conservative with object creation. Use it when it helps, but
don’t make everything an object.
Note: Ruby’s Everything is an object, has to do with consistency of the type
system it doesn’t mean you should go out and create a new class for every
piece of data.

It’s interesting to follow you observertations throughout the project. You
are touching the essential problems of good software design instead of just
mindlessly coding along.

Mikkel

···

“Simon Vandemoortele” deliriousREMOVEUPPERCASETEXTTOREPLY@atchoo.be wrote

Simon,

Validation and formatting gives you more than enough reasons to write smart
getters and setters. Some ideas for you:

  • If country is USA, than only one of the 52 (?) states are allowed
  • If country is USA tha format phone in standard US style
  • International address labels are complex. Streetnumber before or after
    street, for example.
  • If no postal address given, give visit address instead, and the other way
    around, (the latter unless p.o. box involved)
    -output the contact record in vCard 2.1 format for exchange with PDA.
  • The homepage URL attribute might auto-check the url form time to time
    (in a seperate thread, of course)
    and so on.
  • ‘changed’ timestamp, that is really smart and only is set if data has
    really changed (and not at every Save action)

You may want to give your AddressBook class:

  • a persistent ‘current selection’ state
  • multiple level undo’s (there are design patterns for that)

Let us know when the beta is out!

~henq

in message news:Zcdwa.1038$1u5.25@afrodite.telenet-ops.be…

···

“Simon Vandemoortele” deliriousREMOVEUPPERCASETEXTTOREPLY@atchoo.be wrote

The problem I see in this setup is that my ‘Objects’ are very passive;
they are not much more than datastructures with an attitude. Their whole
interface consists of setters and getters.

> Just for a while, forget about classes, typing, and "good design". Totally. > Just code up the simplest code that could work (and surprise yourself at > just how simple that code can be).

that’s pretty good - you should write a book on ruby! :wink:

-a

···

On Wed, 14 May 2003, Dave Thomas wrote:

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

OpenStruct. Cool. I hadn’t seen this before.

···

On Tue, 2003-05-13 at 17:33, Dave Thomas wrote:

  Contact = OpenStruct
   c = Contact.new
   c.name = "Dave Thomas"
   c.tel  = "555 1212"


– Jim Weirich jweirich@one.net http://w3.one.net/~jweirich

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Just for a while, forget about classes, typing, and “good design”.
Totally. Just code up the simplest code that could work (and surprise
yourself at just how simple that code can be).

Well … actually, I was under the impression I was already doing that:
I already have a more or less complete Contact class + TUI client. It’s
only once I have the working code that I start worrying if I really made
the right interface choice. Since I’ve been taught my interface should
not change over time, I find it difficult to dismiss such worries.
I think from now on I’ll go by the name ‘The Overthinker’ :slight_smile:

Sorry to preach :slight_smile:

Right now, I can use all the preaching I can get :>

Thx,
Simon

···

On Tue, 13 May 2003 at 21:33 GMT, Dave Thomas wrote:


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.

                                                -- Blair Houghton

That imposes orderings in which the attributes can be set (e.g. you must set
the country before setting the phone number). It might be a better solution
for the Addressbook itself to perform validation at the point where you
insert a new or updated entry: that way, when you check out an address and
modify it, you can make a series of changes (some of which temporarily leave
the entry in an inconsistent state, like country=UK but postcode=US zip
code), until you write it back.

Alternatively, if you want addresses to be self-validating, it might be
better to have a separate class for each country of interest:

class Address               # general purpose rest-of-world address
class USAddress < Address   # US zip-code and phone number
class UKAddress < Address   # UK postcode and phone number
... etc

where method ‘format_address_label’ does the right thing in each case.

Then you’d have a factory method for creating new addresses, which would
force you to provide the country code first:

MakeAddress('US')   => returns a USAddress
MakeAddress('BE')   => returns an Address

You still wouldn’t be able to modify someone’s address from country X to
country Y: you’d have to create a brand new Address object to replace the
existing Address object. In which case for consistency maybe all Address
objects should be immutable?

Regards,

Brian.

···

On Sat, May 17, 2003 at 05:43:37PM +0900, henq <henq _ replace 0 by o wrote:

Validation and formatting gives you more than enough reasons to write smart
getters and setters. Some ideas for you:

  • If country is USA, than only one of the 52 (?) states are allowed
  • If country is USA tha format phone in standard US style
  • International address labels are complex. Streetnumber before or after
    street, for example.
  • If no postal address given, give visit address instead, and the other way
    around, (the latter unless p.o. box involved)

“ahoward” ahoward@fsl.noaa.gov wrote in message
news:Pine.LNX.4.53.0305132152020.1086@eli.fsl.noaa.gov

that’s pretty good - you should write a book on ruby! :wink:

Well actually - Programming Ruby is a tutorial and reference.

It is not a philosophical journey into the simplicity of Ruby.

I’d like to suggest a title:

“Zen and the Art of Software Maintainance”

Mikkel

Me too! I didn’t notice at first the difference from the earlier Struct
class.

This is a nice improvement over Struct, which required defining the
attributes in an initial Struct statement.

···

On Tuesday, May 13, 2003, at 10:43 PM, Jim Weirich wrote:

On Tue, 2003-05-13 at 17:33, Dave Thomas wrote:

  Contact = OpenStruct
   c = Contact.new
   c.name = "Dave Thomas"
   c.tel  = "555 1212"

OpenStruct. Cool. I hadn’t seen this before.

[snip]

“Simon Vandemoortele” deliriousREMOVEUPPERCASETEXTTOREPLY@atchoo.be
schrieb im Newsbeitrag news:U%lwa.1320$1u5.224@afrodite.telenet-ops.be…

···

On Tue, 13 May 2003 at 21:33 GMT, Dave Thomas wrote:

Just for a while, forget about classes, typing, and “good design”.
Totally. Just code up the simplest code that could work (and surprise
yourself at just how simple that code can be).

Well … actually, I was under the impression I was already doing that:
I already have a more or less complete Contact class + TUI client. It’s

What’s a “TUI client”? Is it just a spelling error or has it something to
do with a travel agency?

robert

Mark Wilson wrote:

OpenStruct. Cool. I hadn’t seen this before.

Me too! I didn’t notice at first the difference from the earlier Struct
class.

Dave’s busy documenting, so I’m getting pretty good at all these library
classes that we skipped the first time around :slight_smile:

It’s an alternative, anyway. I like the Struct in that it
documents the layout of the structure when we initialize
it.

But if you’re not populating all values for every object,
or if (interesting case) you add unforeseen fields via
a data file at runtime, then OpenStruct could be a
great thing.

BTW, it’s not in the core (at least not in my 1.7.3 –
I know I should be ashamed of myself for not being on
the bleeding edge). So require “ostruct” to get it
(those of you who were wondering). It’s a nice tribute
to the simplicity of Ruby and the power of the
method_missing technique (only 55 lines, and could
have been done in fewer).

Hal

···

----- Original Message -----
From: “Mark Wilson” mwilson13@cox.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Tuesday, May 13, 2003 9:47 PM
Subject: Re: OpenStruct (was: [OO design] Objects VS Datastructures)

On Tuesday, May 13, 2003, at 10:43 PM, Jim Weirich wrote:

On Tue, 2003-05-13 at 17:33, Dave Thomas wrote:

  Contact = OpenStruct
   c = Contact.new
   c.name = "Dave Thomas"
   c.tel  = "555 1212"

OpenStruct. Cool. I hadn’t seen this before.

[snip]

Me too! I didn’t notice at first the difference from the earlier Struct
class.

This is a nice improvement over Struct, which required defining the
attributes in an initial Struct statement.

Robert Klemme wrote:

What’s a “TUI client”? Is it just a spelling error or has it something to
do with a travel agency?

I think TUI means ``Text User Interface’'.

Cheers,

···


Laurent

TUI = text user interface
GUI = graphical user interface

···

On Wed, 14 May 2003 14:15:06 +0200, Robert Klemme wrote:

What’s a “TUI client”? Is it just a spelling error or has it something to
do with a travel agency?

http://www.tui.de

robert


Simon Strandgaard

I’ll bet you are. :slight_smile:

I was going to brag that it was in The Ruby Way
but now I see it isn’t! It was supposed to be in
Appendix B, “From Python to Ruby.” Yet another
oversight. So it goes.

Hal

···

----- Original Message -----
From: “Dave Thomas” dave@pragprog.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Tuesday, May 13, 2003 9:59 PM
Subject: Re: OpenStruct (was: [OO design] Objects VS Datastructures)

Mark Wilson wrote:

OpenStruct. Cool. I hadn’t seen this before.

Me too! I didn’t notice at first the difference from the earlier Struct
class.

Dave’s busy documenting, so I’m getting pretty good at all these library
classes that we skipped the first time around :slight_smile:

“Simon Strandgaard” 0bz63fz3m1qt3001@sneakemail.com schrieb im
Newsbeitrag news:pan.2003.05.14.12.11.29.5455@sneakemail.com

What’s a “TUI client”? Is it just a spelling error or has it
something to
do with a travel agency?

http://www.tui.de

robert

TUI = text user interface
GUI = graphical user interface

Thanks! I didn’t know that acronym. (Shame on me) Thought it might be a
spelling error because “T” and “G” are quite near on my keyboard…

Regards

robert
···

On Wed, 14 May 2003 14:15:06 +0200, Robert Klemme wrote: