[OO interface design] Pass-by reference VS encapsulation?

Dear ruby-ists,

I hope you will indulge this slightly (I hope) off-topic design question
since it is not specific to ruby design.

Setting:

Suppose you want to code an addressbook editor. You want it to be based
on Addressbook class that can be reused, so that other people may
write their UI on your addressbook format. How do you go about this ?

My suggestion would be: create a Contact class that contains one persons
information (name, address, phone, …). Then create a container for
Contact instances and call it Addressbook.

I imagine you would want the addressbook to be responsible for the way
it loads, stores and saves its contacts and hide the implementation
details from the world. One could write an interface like:

class Addressbook
def Addressbook.open(file) {…} (takes a block)
def addContact(contact) {…}
def getContact(contact_name) {…}
def deleteContact(contact_name) {…}
end

Problem:

All this works wonderfully well but for one thing: once the addressbook
gives a Contact (actually a reference to Contact) to a client, the
client can modify the Contact (that is inside the addressbook)
without the addressbook’s knowledge. In effect: the addressbook’s
content is open for the world to play with. Overall this could be pretty
much OK if the addressbook didn’t really care about the content of
his contacts. Suppose the addressbook wants to enforce a ‘no two
contacts with same name’ policy ? It can’t because you can always enter an
contact and change it’s name afterwards (behind the addressbook’s back). What if
you want to implement the addressbook as an Array of contacts sorted by
their names ?

Solution proposals:

[1] The ‘pass-by-value emulation’ solution

Emulate pass-by-value by returning a deepcopy of a contact when when
’getContact’ is called. You would also have to take a deepcopy of any
contact added with ‘addContact’ and implement a 'updateContact(contact)'
function that allows one to supersede a contact with a new version.

Contra: to be really safe you have to implement any visitor+iterator
pattern so that the visitor/iterator sees deepcopy’s of the
contacts. (auch!)

[2] The ‘frozen contacts’ solution

Very easy: freeze all contacts that are put in the addressbook and
implement an ‘updateContact(contact)’.

Contra: weird ? (feel ‘hackish’ to me)

[3] The ‘callback’ solution

When a contact is added to the addressbook, have the addressbook
register itself as an observer of the contact’s name with the contact
(and have the contact notify the addressbook if it changes).

Contra: strong coupling of Contact and Addressbook class

[4] The ‘give up abstraction layer’ solution

Maybe a container who’s elements are easily accessible (visitor+iterator
pattern) cannot have interface decoupled with it’s implementation ? This
would mean you might as well implement the Addressbook class as an
array.

Contra: - no hiding of implementation
- ‘no two contacts with same name’ and other policies have to be
implement by the clients

What do you think ?

Simon

···


There are 10 types of people in the world…
those who understand binary and those who don’t.

Simon Vandemoortele wrote:

All this works wonderfully well but for one thing: once the addressbook
gives a Contact (actually a reference to Contact) to a client, the
client can modify the Contact (that is inside the addressbook)
without the addressbook’s knowledge.

Is a Contact that you return as the result of a search(say) the same
type of thing that you use to create or edit a new entry?

You assumption is that they are (basically) the same thing, but perhaps
you might want to rethink that. They don’t seem to share the same
methods. Maybe you need two classes: Contact and MutableContact. Regular
Contact classes have methods that let them be examined but not altered.
MutableContacts allow change. If needed by your application, class
Contact could include a #to_matuable method that returned a MutableContact.

Cheers

Dave

> Suppose you want to code an addressbook editor. You want it to be based > on *Addressbook* class that can be reused, so that other people may > write their UI on your addressbook format. How do you go about this ? > _Problem:_ > > All this works wonderfully well but for one thing: once the addressbook > gives a *Contact* (actually a reference to *Contact*) to a client, the > client can modify the *Contact* (that is _inside_ the addressbook) > without the addressbook's knowledge.

I would suggest using Mephle. (Yes, I finally have an 0.7 release
prepared, I’ll send an announcement this evening. :wink:

While 0.7 does not have ACLs, 0.8 will.

Mephle allows you to create and use objects remotely. This means the
client can do this:

contact = Get(conn, :contactdb, "/contacts/john-doe")

This is now a proxy to the remote object. It will act and behave
exactly like that object. However, if you don’t want the client doing
something (calling a particular method), you merely define an ACL that
excludes them from doing so. That way when they try:

contact.email = "foo@bar.com"

…a PermissionDenied exception gets raised across the network. Of
course, they can procure a local_copy of the object and do whatever
they want to it. It’s their object now. (Given they would have
permissions to acquire a copy—you may not want sensitive data
returned.)

But, it’s a simple and complete solution.

···

On Thu, 1 May 2003 01:55:07 +0900 Simon Vandemoortele deliriousREMOVEUPPERCASETEXTTOREPLY@atchoo.be wrote:


Ryan Pavlik rpav@users.sf.net

“Even the shiniest sword could not save us now!” - 8BT

Quoteing deliriousREMOVEUPPERCASETEXTTOREPLY@atchoo.be, on Thu, May 01, 2003 at 01:55:07AM +0900:

I hope you will indulge this slightly (I hope) off-topic design question
since it is not specific to ruby design.

I’ve run into this issue a bunch, in C and again in Ruby, when building
decoders for things like X.509 certificates, and vCards. It’s
surprisingly hard to think through, I found. What I’ve done (and I’m not
claiming its the best way!), is massage my assumptions about the
requirements so that I solve just the problem I actually have, not all
the problems I can imagine might be possible to solve.

I hope this will pertinent, so I’ll describe what i did in various
circumstances. It’s a little rambly, but I have lots of thoughts, and
no time to edit.

** X.509 certs - I made them immutable, essentially. When you decode,
you do a DecodeBegin(), get a decode context, and it is read-only. This
makes sense, because:

  • you can’t change anything in a cert without invalidating the
    signature!
  • its rare to want to make a new cert from an old one, I.e, to change
    it a little and resign it

Creating a cert is a different process, you do a EncodeBegin(), and
start adding the things you want in the cert, and when you’re done,
call End(), and it gives you the binary DER encoding for the cert.

We began the design process with the idea that we would just have
a Certificate object, and it would be mutable, and you could create
one from the binary encoding, then change it, then encode it again.
This turned out to be hard, in lots of ways, so we rethought it,
and realized the model was not-right, it sounded nice and OOy, and
academic, but really, certificate encoding and decoding are very seperate
processes, not intermixed (usually) in the code. CAs encode. Everybody
else just decodes! The objects should be different (Dave’s comment
about it being the same object only if the data AND the operations
are identical applies here).

** XML - I didn’t write this, but look at REXML (I don’t know it well,
so hopefully I’m not wrong about this!). What REXML appears to do is
decode XML into an object hierarchy. You can then change anything (?) in
the hierarchy (add elements, remove, change them). Then you reencode
from the doc element route, and it traverses the hierarchy, reencoding
everything.

Imagine if you had to do element.get_mutable_element() to get a mutable
element from the tree? It would be hugely painful! And you actually want
to modify some things inplace, not get a mutable version of a whole
document, or a sub-tree of the document.

The approach of REXML is that you can’t make a bad XML element, they are
all valid XML elements, so the containing document doesn’t need any
control over the parts, the parts are all valid XML.

—> This is one approach for your Contact. Abandon, your idea of having
the AddressBook enforce some kind of conditions. Any condition you name,
I think I could argue that its too limiting! Your example of no
duplicate names certainly is. Looked at another way, why should one
Contact be coupled to ANY other contact? There may be things that a
Contact will not allow (such as setting the name to nil, for example),
but if its a good contact, why would there be some state in the
AddressBook that causes my Contact to not be a valid member of it? Why
even disallow duplicates? What’s a duplicate? There can be two Tom Jones
living in the same house, with the same phone number!

In REXML’s case, imagine that a REXML document was validating, that it
had a DTD, and wouldn’t allow you to add an element of a particular tag
in a place that the DTD didn’t allow. Ouch! That’s a hard problem, and
it doesn’t try to solve it. I think it would be pretty hard. Every
modification to an element would have to check the DTD to make sure its
a valid modification.

** vCard - I recently wrote a vCard decoder/encoder. I wanted a single
object (I didn’t want to split encoding and decoding), because a vCard
is a contact, and people change contacts. I also wanted to, as much
as possible, preserve the original encoding. This is because reencoding
wire formats is a BAD idea, it usually leads to the telephone game,
where one person whispers in someones ear, who whispers in another,
and what comes out isn’t what goes in. In theory, if everybody
does it perfectly, it works, but assuming perfection is a fast route to
failure. The PKI and email worlds are full of bugs and interoperability
problems caused by reencoding. Anhow, that means that I didn’t want
to just decode to a hierarchy of objects, allow them to be changed,
and then during reencoding walk the tree and reencode everything. I only
want to reencode pieces that are new, or have been changed.

Background: a vCard is decoded internally into and array of objects,
where the object contains a hash of paramaters, where each paramater has
an array of values (a email address can have a type=home,internet,pref,
for example).

card
original String
lines
Line { params{ ‘name’ => [ value1, value2, …] }, other things…

I wanted people to be able to change a vCard. That meant changing
anything, and if they changed a Line, the Card needed to know, so that
it would know it had to reencode itself, otherwise it would leave the
original encoding unchanged. I implemened the params, for example, as a
hash mapping String to an Array. How would the Card know that a Line
that it had returned as a result of search was changed? How would the
Line know that a piece of its parmas Hash had changed? And some of the
params effect the contents of a line - if you add a param that say the
enoding is base-64, the line’s value has to be changed to base-64.

Approach 1:

Don’t return Ruby base types like a Hash of String=>Array, write my
own types, that all could have Observers, and that would notify there
containing objects when they were changed. I could (and would) have
done this, and it would have worked fine. But it would have been a lot
of work, and I’m using Ruby to do less work, not more! I don’t want to
build wrappers for Array and Hash!

→ Another post mentioned a variant of this idea for a Contact, where
a Contact is some kind of Facade, and all requests made to it are
forwarded to the AddressBook, which has enough information to say
whether it is valid.

Approach 2:

Change my assumptions. I made a Line immutable. You can add a Line,
you can delete a Line, you can find a Line, you can create a new Line,
but you can’t modify a Line. If you want to change a Line, you have to
make a new Line and add it to the Card, and delete the old Line from
the card. Now creation happens in one way, and during the creation of a
new Line I can apply all the self-consistency tests (encoding
specified once, if the encoding is base-64, encode the value as
base-64, etc, etc.). Adding also happens in one place, and I can check
vCard consistency there (no adding of a Line saying BEGIN:vCard into
the middle of a vCard!).

So, my implementation is simpler, faster to write, and thus less buggy
(I hope), and easier to maintain. Is it as amzing as it could have
been? No, but I don’t have 3 months to work on amazing… And its
pretty easy to do the thing that need to be done.

→ A variant of this approach for you: Have a AddressBook.find that returns
a Contact that is a duplicate of the Contact it has internally. Allow the
Contact to be changed, but since this is a stand-alone object, it
doesn’t affect the AddressBook’s contact entry. Nothing is saved until
you do a AddressBook.save(contact). The AddressBook could then do
any validation it wanted to in the save, rather than every single change
you make to a Contact needing to be validated.

I think variants of this problem show up all over the place, and have a
lot of different solutions and approaches, its really interesting to
hear people talking about it!

Sam

Is a Contact that you return as the result of a search(say) the same
type of thing that you use to create or edit a new entry?

Yes.

You assumption is that they are (basically) the same thing, but perhaps
you might want to rethink that. They don’t seem to share the same
methods. Maybe you need two classes: Contact and MutableContact. Regular
Contact classes have methods that let them be examined but not altered.
MutableContacts allow change. If needed by your application, class
Contact could include a #to_matuable method that returned a MutableContact.

Hmmm. I never thought about that, mainly because it requires creating
two types of contacts that will hold exactly the same data. I will
take it into consideration.

Simon

PS: When I have complex problems like this one, I have a habit of
writing them out as a post to clarify my thinking. I often let it simmer
a while and rewrite it before I post. This time however I inadvertently
posted the message before I got a chance to rewrite it :>. (I canceled
the post immediately but it seems it did get to ruby-talk.)

For those of you who are interested: this is what I was going to post
before I got Dave’s reply:

Goal: An Addressbook class that can be reused.

Initial Design and shortcomings:

The way I see it, an addressbook is merely a (smart) container for
contacts. It is responsible for loading, storing, managing (adding,
deleting) and saving the contacts. Thus, the interface could be
something like:

Addressbook

  • open(filename)
  • addContact(contact)
  • getContact(name)
    `- removeContact(name)

Contact

-setName
-getName
-setAge
-getAge
-…

This approach provides a lot of flexibility - you can change a
contact with a one-liner:

myAddressbook.getContact(‘Simon Vandemoortele’).setAge(25).

However, it leaves the content of addressbook (the contacts) freely
editable by everyone. I hope we all agree to the fact that this is a
serious limitation to the interface. Because someone can change a
contact without the addressbook noticing, the addressbook cannot enforce
any invariant that depends on it’s contacts content. i.e.: it can’t
enforce a no-two-entries-with-the-same-name policy. Nor can we decide to
implement the addressbook as a sorted Array anymore.

Alternative designs:

I am told by learned colleagues that the OO take on this kind of
problems is to introduce immutability.

[1] The classical way to do this is by making the interface immutable
(no writer methods). So ‘setAge’ would not change the contact’s name but
instead create a copy of the instance with an updated age attribute:

myContact = myAddressbook.getContact(‘Simon Vandemoortele’)
myContact = myContact.setAge(25)
myAddressbook.updateContact(myContact)

This can be quite cumbersome.

[2] In Ruby there might be a different way to do it: require that all
contacts submitted to the addressbook be frozen. That way you can
enforce immutability without losing the convenience of attribute writers
on Contact. The drawback I see is that the immutability is not really
enforced in the interface: a client can still try to change a frozen
contact, something that is impossible in [1].

Questions:

Am I right in going the immutable way here ? (I’ve thought of 2 other
solution to the shortcomings of the first design: having the contacts
cooperate with the addressbook or using deepcopy when adding and
deleting contacts.)

If so, which implementation would you chose ?

···

On Wed, 30 Apr 2003 at 18:07 GMT, Dave Thomas wrote:


There are 10 types of people in the world…
those who understand binary and those who don’t.

Simon Vandemoortele wrote:

Is a Contact that you return as the result of a search(say) the same
type of thing that you use to create or edit a new entry?

Yes.

See below… :slight_smile:

Your assumption is that they are (basically) the same thing, but perhaps
you might want to rethink that. They don’t seem to share the same
methods. Maybe you need two classes: Contact and MutableContact. Regular
Contact classes have methods that let them be examined but not altered.
MutableContacts allow change. If needed by your application, class
Contact could include a #to_matuable method that returned a MutableContact.

Hmmm. I never thought about that, mainly because it requires creating
two types of contacts that will hold exactly the same data. I will
take it into consideration.

Remember: objects equal data + operations. Two objects have different
types if they differ in either.

Also remember, their may be a subclassing or delegation interface
between the mutable and immutable interfaces; there’s nothing to say
that you have to duplicate information.

Cheers

Dave

···

On Wed, 30 Apr 2003 at 18:07 GMT, Dave Thomas wrote:

i would not agree with these statements. it is not impossible to maintain the
referential integrity of the address book and to allow unfettered access to
contacts. all that is required is

a) contacts can be aware that they are ‘contained’ (in an address book)
b) contact methods use this awareness when ri can be violated

eg (silly demo impl).

----CUT----
class Addressbook < Array
alias __append <<
def << contact
check_contraint ‘<<’, contact
__append(contact)
sort!
contact.addressbook = self
self
end
def get_contact name;map{|c| return c if c.name == name};end
def check_contraint constraint, arg
CONSTRAINTS[constraint].call self, arg
end
protected
CONSTRAINTS = Hash[
‘Contact#name=’ =>
Proc.new do |addressbook, name|
raise ‘name exists’ unless
addressbook.select{|contact| contact.name == name}.empty?
end,
‘<<’ =>
Proc.new do |addressbook, contact|
raise ‘contact exists’ unless
addressbook.select{|c| c.name == contact.name}.empty?
end,
]
end

class Contact
include Comparable
attr :name
attr_accessor :addressbook
def initialize args;self.name=(args[:name]);end
def name= name
addressbook and addressbook.check_contraint ‘Contact#name=’, name
@name = name
end
def <=> other; name <=> other.name; end
end

addressbook = Addressbook.new
c0 = Contact.new :name => ‘bob’
c1 = Contact.new :name => ‘bob’
c2 = Contact.new :name => ‘joe’

addressbook << c0 << c0 rescue p $!
((addressbook << c2).get_contact(‘joe’)).name = ‘bob’ rescue p $!
----CUT----

which will output:

~/eg/ruby > ruby addressbook.rb
#<RuntimeError: contact exists>
#<RuntimeError: name exists>

this does, however, tightly couple contacts with addressbooks. but aren’t
they tightly coupled in the ‘real’ world? i can see alternatives to this type
of impl, such as making to container itself enfore ri, such as

class Addressbook < RBTree
def << contact
self[contact.name] = contact
end
end

which would allow only one contact with the same name.

alternatively, you could implement a module which enfores RI for mutable
methods and extend any contact added to an addresssbook with this module -
contacts outside of an addressbook do not need RI after all.

in the end - if contacts are aware they are IN an addressbook i think you can
have your cake (easy access) and eat it too (safe access). again, i think any
design will require some coupling of contacts and addressbooks since
contacts are related the context of an addressbook, but not outside of it
(eg. contact ‘bob’ could be in addressbook A and in addressbook B).

this is a very interesting topic IMHO - at least for anyone attempting to do
OO desgin - and this is all ruby programmers right? :wink: and therefore on
topic!

-a

···

On Wed, 30 Apr 2003, Simon Vandemoortele wrote:

Goal: An Addressbook class that can be reused.

Initial Design and shortcomings:

The way I see it, an addressbook is merely a (smart) container for
contacts. It is responsible for loading, storing, managing (adding,
deleting) and saving the contacts. Thus, the interface could be
something like:

Addressbook

  • open(filename)
  • addContact(contact)
  • getContact(name)
    `- removeContact(name)

Contact

-setName
-getName
-setAge
-getAge
-…

This approach provides a lot of flexibility - you can change a
contact with a one-liner:

myAddressbook.getContact(‘Simon Vandemoortele’).setAge(25).

However, it leaves the content of addressbook (the contacts) freely
editable by everyone. I hope we all agree to the fact that this is a
serious limitation to the interface. Because someone can change a
contact without the addressbook noticing, the addressbook cannot enforce
any invariant that depends on it’s contacts content. i.e.: it can’t
enforce a no-two-entries-with-the-same-name policy. Nor can we decide to
implement the addressbook as a sorted Array anymore.

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

Remember: objects equal data + operations. Two objects have different
types if they differ in either.

True (my OO-hat is a little dusty …).

Also remember, their may be a subclassing or delegation interface
between the mutable and immutable interfaces; there’s nothing to say
that you have to duplicate information.

Yes, now I see what it could be like (using either subclassing or
delegation). Thanks.

Just out of curiosity: would you comment on the two alternative designs
I mentioned in my previous reply ? (It is rather appropriate that the
LotY should be Haskel.)

Greetings,
Simon

PS: Since this is our first mail exchange I figure I am entitled to a
short I-read-your-books-and-loved-them-,-thanks-for-everything
statement :slight_smile:

···

On Wed, 30 Apr 2003 at 19:35 GMT, Dave Thomas wrote:


There are 10 types of people in the world…
those who understand binary and those who don’t.

However, it leaves the content of addressbook (the contacts) freely
editable by everyone. I hope we all agree to the fact that this is a
serious limitation to the interface. Because someone can change a
contact without the addressbook noticing, the addressbook cannot enforce
any invariant that depends on it’s contacts content. i.e.: it can’t
enforce a no-two-entries-with-the-same-name policy. Nor can we decide to
implement the addressbook as a sorted Array anymore.

i would not agree with these statements. it is not impossible to maintain the
referential integrity of the address book and to allow unfettered access to
contacts. all that is required is
a) contacts can be aware that they are ‘contained’ (in an address book)
b) contact methods use this awareness when ri can be violated

Yes, that is what I meant by a solution where ‘the contacts cooperate
with the addressbook’.

this does, however, tightly couple contacts with addressbooks. but aren’t
they tightly coupled in the ‘real’ world?

I don’t know … but I do feel the potential for re-using the Contact
class outside the Addressbook is rather limited anyway so I wouldn’t
feel too bad about coupling them.

in the end - if contacts are aware they are IN an addressbook i think you can
have your cake (easy access) and eat it too (safe access). again, i think any
design will require some coupling of contacts and addressbooks since
contacts are related the context of an addressbook, but not outside of it
(eg. contact ‘bob’ could be in addressbook A and in addressbook B).

I am not sure I would agree here … a contact can perfectly exist by
itself and have meaning on it’s own. Moreover, contacts have no inkling
whatsoever they are being contained, managed and saved by an addressbook
in my current implementation.

this is a very interesting topic IMHO - at least for anyone attempting to do
OO desgin - and this is all ruby programmers right? :wink: and therefore on
topic!

I couldn’t agree more.

Thank you very much for the time you took to reply in such a complete
and elaborate manner.

  • Simon
···

On Wed, 30 Apr 2003 at 20:29 GMT, ahoward wrote:


There are 10 types of people in the world…
those who understand binary and those who don’t.

That’s a very cool idea. Since the RI methods are in the object’s singleton
class, doing a ‘.dup’ will give you a fresh object without the RI methods -
in otherwords it copies a contact which is ‘bound’ into the addressbook and
gives you another object which is ‘free’.

I am not sure about allowing one contact object to be shared between two
addressbooks though. Any change to the contact would have to be ‘agreed’ by
both addressbooks, which sounds like a fairly complicated two-phase commit
to avoid getting into an inconsistent state.

In the end it may be simpler to take a copy and check it back into the
addressbook(s) after any changes have been made. Sort of like having a
‘post-it note’ onto which you scribble the details, and then write them back
after editing. It involves data copying but it’s how things would work if
you were using a distributed system like DRuby anyway.

Regards,

Brian.

···

On Thu, May 01, 2003 at 05:37:46AM +0900, ahoward wrote:

alternatively, you could implement a module which enfores RI for mutable
methods and extend any contact added to an addresssbook with this module -
contacts outside of an addressbook do not need RI after all.

Simon Vandemoortele wrote:

Just out of curiosity: would you comment on the two alternative designs
I mentioned in my previous reply ? (It is rather appropriate that the
LotY should be Haskel.)

To be honest, I’m not sure anyone can comment on alternative approaches
without knowing the context: what is the problem that’s being solved.

I’m not sure why you feel the immutable direction is cumbersome
(although I probably wouldn’t do it exactly the way you show.

myContact = myAddressbook.getContact(‘Simon Vandemoortele’)
myContact = myContact.setAge(25)
myAddressbook.updateContact(myContact)

My code might look something like:

contact = book.mutable_contact_for_name(‘Simon Vandemoortele’)
contact.age = 25
contact.save

I’m worried in your approach that it’s too easy to lose the mutable
object: on first sight

myContact = myAddressbook.getContact(‘Simon Vandemoortele’)
myContact.setAge(25)
myAddressbook.updateContact(myContact)

looks reasonable, but it discards the change.

So, I’d have a method in Contact that changes an immutable Contact into
a mutable one. I’d also add factories in AddressBook that could return
these directly. I’d then not even implement an ‘age=’ method in a
immutable Contact class.

For simplicity I’d also have AddressBook set itself up as the backing
store for Contacts it creates. That way you just have to write
‘contact.save’ rather than carry both the Contact and AddressBook
objects around throughout your code.

PS: Since this is our first mail exchange I figure I am entitled to a
short I-read-your-books-and-loved-them-,-thanks-for-everything
statement :slight_smile:

Thank you!

Cheers

Dave

Brian Candler B.Candler@pobox.com wrote in message news:20030430212939.GB41345@uk.tiscali.com

I am not sure about allowing one contact object to be shared between two
addressbooks though. Any change to the contact would have to be ‘agreed’ by
both addressbooks, which sounds like a fairly complicated two-phase commit
to avoid getting into an inconsistent state.

Not really complicated. Add Vetoable Change Support to the Contact
class. The involved addressbooks register a Vetoable Change Listener
at the contact objects. Whenever a contact is changed, all registrated
listeners are allowed to vote about the change, and they can prevent
that the change takes place.

Well, you could have a base class “Contact” and a derived class
“BoundContact” (sorry, can’t think of a better name :slight_smile:

silly example:

–CUT–
class Contact
def initialize(someValues)
@name = someValues[‘name’]

end

def setName(aName)
@name = aName
end

def to_h
{
‘name’ => @name

}
end

def putInAddressBook(aBook)
return BoundContact.new(aBook, to_h)
end
end

class BoundContact < Contact
def initialize(anAddressBook, someValues)
@addressBook = anAddressBook
super
end

def setName(aName)
unless @addressBook.findContact(aName)
super
else
raise “Contact ‘#{aName}’ already exists”
end
end

def getUnboundContact
return Contact.new(to_h)
end
end
–CUT–

silly example, using encapsulation:

–CUT–
class Contact
def initialize(someValues)
@name = someValues[‘name’]

end

def setName(aName)
@name = aName
end

def to_h
{
‘name’ => @name

}
end

def putInAddressBook(aBook)
return BoundContact.new(aBook, self)
end
end

class BoundContact
def initialize(anAddressBook, aContact)
@addressBook = anAddressBook
@contact = aContact
end

def setName(aName)
unless @addressBook.findContact(aName)
@contact.setName(aName)
else
raise “Contact ‘#{aName}’ already exists”
end
end

def getUnboundContact
return @contact
end
end
–CUT–

···

On Wed, 30 Apr 2003 21:17:23 +0000, Simon Vandemoortele wrote:

a) contacts can be aware that they are ‘contained’ (in an address book)
b) contact methods use this awareness when ri can be violated
Yes, that is what I meant by a solution where ‘the contacts cooperate
with the addressbook’.
I don’t know … but I do feel the potential for re-using the Contact
class outside the Addressbook is rather limited anyway so I wouldn’t
feel too bad about coupling them.


“Any time you’ve got both the pope and the Dixie Chicks against you,
you’re not long for the White House”

– Michael Moore

To be honest, I’m not sure anyone can comment on alternative approaches
without knowing the context: what is the problem that’s being solved.

I am simply writing my own addressbook editor (as an exercise and
because I am dissatisfied with existing programs). I thought I’d store it
in a file and make a nice Addressbook class that allows me to access that
data without worrying about where it is saved and it what format. It
should be re-usable so that other people who want to write their own UI
interface to my addressbook can do so using this class instead of
parsing the file. The addressbook will also support internal iterators
and a sophisticated search mechanism (à la mutt).

So, I’d have a method in Contact that changes an immutable Contact into
a mutable one.

By ‘changing into’ you mean create a new instance right ? (Methinks we
would be in trouble if an immutable contact could magically become
mutable in the hands of a client.)

For simplicity I’d also have AddressBook set itself up as the backing
store for Contacts it creates. That way you just have to write
‘contact.save’ rather than carry both the Contact and AddressBook
objects around throughout your code.

That looks very practical but it does induce strong coupling of the
Addressbook and Contact class. Once we go in that direction I don’t see
why I shouldn’t stick with my original (fully mutable) implementation
where contacts callback on the addressbook when they get a request to
change.
Besides: what would happen if you call #save on a contact and the
addressbook refuses the save because there’s already an entry with that
name ? Wouldn’t it be better if the Contact consulted the addressbook as
soon as the duplicate name was entered ? (and not after all sorts of
other attributes have been set on the contact)

From everyone’s silence on the freeze-your-contacts alternative I
assume it is more of a hack than a design solution.

Greetz,

  • Simon
···

On Wed, 30 Apr 2003 at 21:12 GMT, Dave Thomas wrote:


There are 10 types of people in the world…
those who understand binary and those who don’t.

I don’t think that’s sufficient. Other things could change in the period
between the go/no-go response and the requested change taking place, unless
you serialise everything.

An addressbook is not a particularly good example, but it can demonstrate
the point. Let’s say that ‘name’ is the primary key and the only constraint
for an addressbook is that all objects it contains have distinct names.

Address book A contains:  'fred' (shared), 'anne'
Address book B contains:  'fred' (shared), 'bob'

Client X says to A “'I want to change name of ‘fred’ to ‘jim’”

  • address book A responds ‘OK’

Client Y inserts another ‘jim’ into A

Client X asks address book B the same question

  • address book B responds ‘OK’

Client X changes ‘fred’ to ‘jim’ [CONFLICT reported by A]

Client Y inserts another ‘fred’ into A

Now X cannot rollback to ‘fred’ without also causing a conflict.

Like I say, not a particularly good example…

Regards,

Brian.

···

On Thu, May 01, 2003 at 08:20:18PM +0900, Nico Hoogervorst wrote:

Brian Candler B.Candler@pobox.com wrote in message news:20030430212939.GB41345@uk.tiscali.com

I am not sure about allowing one contact object to be shared between two
addressbooks though. Any change to the contact would have to be ‘agreed’ by
both addressbooks, which sounds like a fairly complicated two-phase commit
to avoid getting into an inconsistent state.

Not really complicated. Add Vetoable Change Support to the Contact
class. The involved addressbooks register a Vetoable Change Listener
at the contact objects. Whenever a contact is changed, all registrated
listeners are allowed to vote about the change, and they can prevent
that the change takes place.

Simon Vandemoortele wrote:

So, I’d have a method in Contact that changes an immutable Contact into
a mutable one.

By ‘changing into’ you mean create a new instance right ? (Methinks we
would be in trouble if an immutable contact could magically become
mutable in the hands of a client.)

It’s be a new instance of a new class.

For simplicity I’d also have AddressBook set itself up as the backing
store for Contacts it creates. That way you just have to write
‘contact.save’ rather than carry both the Contact and AddressBook
objects around throughout your code.

That looks very practical but it does induce strong coupling of the
Addressbook and Contact class.

Not particularly: the Contact object you return could actually be a
StorableObject that wraps the actualy contact object and adds the #save
method: Contact need know nothing about the AddressBook.

Once we go in that direction I don’t see
why I shouldn’t stick with my original (fully mutable) implementation
where contacts callback on the addressbook when they get a request to
change.

Validation might be an issue: see below.

Besides: what would happen if you call #save on a contact and the
addressbook refuses the save because there’s already an entry with that
name ? Wouldn’t it be better if the Contact consulted the addressbook as
soon as the duplicate name was entered ? (and not after all sorts of
other attributes have been set on the contact)

It depends on your application’s semantics. Most of the time, objects
representing persistent state are allowed to be inconsistent for a short
time with repect to the database: they only come in to line when you try
to persist them. This means that cross-field validation is often
performed at a commit point (such as the save). Otherwise you run in to
problems when two fields interact in the validation process.

Think of it the other way around: if you’re worried about folks updating
contacts that they receive when they don’t really mean to persist those
changes, would you want the default behavior to be to store all changes
that are made? :slight_smile:

Cheers

Dave

Simon Vandemoortele deliriousREMOVEUPPERCASETEXTTOREPLY@atchoo.be writes:

Besides: what would happen if you call #save on a contact and the
addressbook refuses the save because there’s already an entry with that
name ?

Hopefully, it wouldn’t do that. Or are you assuming you’ll never meet
two people named Doug Jones?

Wouldn’t it be better if the Contact consulted the addressbook as
soon as the duplicate name was entered ? (and not after all sorts of
other attributes have been set on the contact)

Why should the Contact care if there’s another object with the same
data it has? It’s the AddressBook’s job to tell you things like
that. One possible solution is to use an Observer pattern to allow
the AddressBook to register itself as being interested in operations
like changing names and optionally disallow them. That way you can
pass around all the mutable object you like, and let the AddressBook
validate any prospective changes.

In this particular case, though, I’d invite you to look through a
phonebook of even a relatively small city and see how many duplicated
names there are. Having some sort of unique key that is not a
person’s name would be a better solution, IMO.

If you’d like to pass around an object and be sure it’ll never get
altered by its reciever, then a Contact/MutableContact pair seems
unavoidable.

-=Eric

···


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

Why should the Contact care if there’s another object with the same
data it has? It’s the AddressBook’s job to tell you things like
that. One possible solution is to use an Observer pattern to allow
the AddressBook to register itself as being interested in operations
like changing names and optionally disallow them. That way you can
pass around all the mutable object you like, and let the AddressBook
validate any prospective changes.

That’s certainly a simple solution to my problem. It’s my impression
though that Dave would go more toward the immutable/mutable +
transaction concept. I am guessing this is for extendability, robustness
reasons and I am quite confident this is a good idea. With my limited
experience however, I am unable to ‘see’ why; I am trying to envision
how the mutable solution would limit me in the future but can’t think of
anything. (But then, people with limited experience are more
shortsighted.)

In this particular case, though, I’d invite you to look through a
phonebook of even a relatively small city and see how many duplicated
names there are. Having some sort of unique key that is not a
person’s name would be a better solution, IMO.

I’ve thought about that but it is my belief that one could very easily
get away with using names as ID’s in the context of a single person’s
addressbook
which is what I am designing. The reason is that human
beings tend to use names as ID’s for the people they know. When they
know two people with the same name, referring to these people tends to
get confusing and inefficient. This is best solved by using nicks or
some form of name alteration to distinguish the two people in a way that
makes sense to oneself and possibly others.

If you’d like to pass around an object and be sure it’ll never get
altered by its reciever, then a Contact/MutableContact pair seems
unavoidable.

Indeed.

Simon

PS: As we evolve in this discussion I find myself frightened by the
complexity of something that, at first, seemed a rather trivial design.
It is starting to look as if the effort required to make the Addressbook
I am implementing really safe, is more than what I am willing to put
into it … especially since there is a chance it will never be used by
any other client than the one I am designing. How does one tell if one
is too lazy too think out a decent (future-proof) design or if one is
sinning against the YAGNI I idiom ?

···

On Wed, 30 Apr 2003 at 22:35 GMT, Eric Schwartz wrote:


There are 10 types of people in the world…
those who understand binary and those who don’t.

Simon Vandemoortele wrote:

That’s certainly a simple solution to my problem. It’s my impression
though that Dave would go more toward the immutable/mutable +
transaction concept. I am guessing this is for extendability, robustness
reasons and I am quite confident this is a good idea. With my limited
experience however, I am unable to ‘see’ why; I am trying to envision
how the mutable solution would limit me in the future but can’t think of
anything. (But then, people with limited experience are more
shortsighted.)

Actually, if I was writing it for myself, I personally wouldn’t do any
of this. This discussion came about because you wanted somehow to
differentiate read-only from read-write versions of Contacts, as you
wanted changes to contacts to ripple through to the backing store. We
went down the (im)mutable path to satisfy that requirement.

However, that’s not the way I’d go. Instead I’d make the objects all
mutable, and have a ‘save’ method that stored them away on a change.
This is what I did last year when I wrote a fairly large web-based
registration and ordering system in Ruby.

Cheers

Dave

simon-

perhaps you’ve already considered this, but why not simply use an embedded
database (guy’s bdb wrapper is great) - this neatly solves the entire problem
of mutable/not-mutable since query results can be mapped to objects that the
client can tweak to their heart’s content, and also makes the entire design
trivial where referential integrity is concerned. just thought i’d through
that out there…

----CUT----
#!/usr/bin/env ruby
require ‘sqlite’
Contact = Struct.new(‘Contact’, :name, :number, :address)

class AddressBook
def initialize args=nil
db = args && args[:db] || ‘addressbook.db’
create = args && args[:create]
@db = SQLite.new(db)
if create
sql = <<-sql
create table addressbook (
name, number, address,
primary key (name)
);
sql
@db.exec (sql)
end
end
def add_contact contact
values = contact.values.map{|v| “‘#{v}’”}.join(‘,’)
sql = <<-sql
insert into addressbook
values (#{values});
sql
@db.exec (sql)
end
def get_contact name
sql = <<-sql
select * from addressbook
where name = ‘#{name}’ limit 1;
sql
fieldnames, values = @db.exec (sql)
Contact.new *values
end
end

ab = AddressBook.new :db => ‘ab.db’, :create => true
c = Contact.new ‘bob’, ‘303.455.2099’, ‘42 Answer St.’
ab.add_contact c
mutable = ab.get_contact ‘bob’
mutable.name = ‘joe’

p mutable
p ab.get_contact ‘bob’
----CUT----

~/eg/ruby/sqlite > ruby ./addressbook.rb
#<Struct::Contact name=“joe”, number=“303.455.2099”, address=“42 Answer St.”>
#<Struct::Contact name=“bob”, number=“303.455.2099”, address=“42 Answer St.”>

-a

···

On Thu, 1 May 2003, Simon Vandemoortele wrote:

If you’d like to pass around an object and be sure it’ll never get
altered by its reciever, then a Contact/MutableContact pair seems
unavoidable.

Indeed.

Simon

PS: As we evolve in this discussion I find myself frightened by the
complexity of something that, at first, seemed a rather trivial design.
It is starting to look as if the effort required to make the Addressbook
I am implementing really safe, is more than what I am willing to put
into it … especially since there is a chance it will never be used by
any other client than the one I am designing. How does one tell if one
is too lazy too think out a decent (future-proof) design or if one is
sinning against the YAGNI I idiom ?

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