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

Something like this ?

#+v
c = myAddressbook.getContact(‘Simon Vandemoortele’)

c.age = 25 | Inconsistent
c.name = “delirious” | phase

begin
c.save // Make data consistent again
rescue DuplicateEntryError
puts “There’s already an entry by that name in the addressbook.”
end
#-v

Simon

···

On Thu, 01 May 2003 at 12:20 GMT, Dave Thomas wrote:

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.


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

“Dave Thomas” dave@pragprog.com wrote in message
news:3EB11114.8090605@pragprog.com

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.

This is essentially also how a record works in a database query in the
Microsoft DAO / ADO object models (known from MS Access and as wrappers
around OleDB).
You read through the set of returned records. The recordset could be created
by searching a name. If you wish to change a contact, you call the Edit
method. Then modify the fields and eventually call Update. Transactions can
be wrapped around this.
This is what I did many years ago for a multiuser address database system
sitting on single Access file. To handle the multi-user scenario I listened
to conflicts reported on Update (and quite a few other scenarios).

In a non-distributed OO design I would not be overly concerned with the
potential dangers of some external piece of source changing the Contact
object in the original design proposal. Things can easily become
over-engineered. If access rights become a concern later, a private flag can
control access mode and update methods can check this flag. If there is no
write access an exception is thrown. Doing the check against access rights
can also be extended to generate notifications against listeners (such as
the AddressBook and potentially all interested clients).

At the point where access control becomes important, you are probably
working with SOAP, RCP, sockets or similar interprocess communication
facilities and you then have a host of other concerns.

Mikkel

Simon Vandemoortele wrote:

Something like this ?

c = myAddressbook.getContact(‘Simon Vandemoortele’)

c.age = 25 | Inconsistent
c.name = “delirious” | phase

begin
c.save // Make data consistent again
rescue DuplicateEntryError
puts “There’s already an entry by that name in the addressbook.”
end

Yup. I’d then think hard about whether I wanted a class AddressBook, or
whether I just have a bunch of contacts. This is not an obvious choice,
and to some extent it depends on where you want to take the application
in the future. However, something to consider might be:

contact = Contact.with_name(‘Simon V’)
contact.age = 25
contact.save rescue …

(Of course you’d never really store ‘age’… )

Cheers

Dave

I agree. Since this is to be a lightweight non-distributed Personal
Addressbook I would certainly go this way.

There is one thing that still puzzles me though: whether to allow for a
‘forbidden/incomplete’ object to exist at any point or not. Suppose I
decide that a Contact instance without a ‘Name’ is utterly pointless and
should not be entered in the addressbook at all. It is easy to have
contacts refuse inconsistent or forbidden data (like an empty name or a
wrong birth-date format) but how do I make sure all contacts that are
created are (or become) consistent ?

Given the way I am currently going with this design I see 2 ways to
implement this:

[1] Make it impossible to create an ‘incomplete’ contact by requiring
the name at construction (and making sure it cannot be set to nil or “”
afterward).

[2] Allow clients to create an ‘incomplete’ (nameless) draft version of
the contact and have them call some #validate function that checks
the completeness of the contact and creates the definitive version.

I would like to go for [1] since it is very simple but in many cases it
just shifts the complexity to the client since the client now has to
buffer data in a structure that probably looks a lot like Contact until
it has enough to create a valid contact.
So I guess I should use [2] but I don’t know how to do that … do I
create two classes of objects: IncompleteContact and Contact. Generate
the second from the first through validation ?

Still a long way to go …

  • Simon
···

On Thu, 01 May 2003 at 16:28 GMT, MikkelFJ wrote:

In a non-distributed OO design I would not be overly concerned with the
potential dangers of some external piece of source changing the Contact
object in the original design proposal. Things can easily become
over-engineered. If access rights become a concern later, a private flag can
control access mode and update methods can check this flag. If there is no
write access an exception is thrown. Doing the check against access rights
can also be extended to generate notifications against listeners (such as
the AddressBook and potentially all interested clients).


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

Yup. I’d then think hard about whether I wanted a class AddressBook, or
whether I just have a bunch of contacts. This is not an obvious choice,
and to some extent it depends on where you want to take the application
in the future. However, something to consider might be:

contact = Contact.with_name(‘Simon V’)
contact.age = 25
contact.save rescue …

I think you are showing me the contact’s persistence need not
necessarily be the Addressbook responsibility, a contact could persist
itself if it mixed in a StorableObject module ?
So instead of telling my addressbook to save all contacts at the end of
the application, every contact would be saved to file as soon as #save
was called on it …

Wouldn’t I still need an object that keeps track of all the instantiated
contacts. How else am I going to generate a sorted list of contact names
for the UI ? Who is going to answer sophisticated search queries (give
me all contacts born between 1977 and 1987.) I could achieve this using
the ObjectsSpace in my client but is that really good design ?
Especially since many clients are probably going to want that query
functionality …

(Of course you’d never really store ‘age’… )

Why not , all I have to do is remember to run
Addressbook.each {|c| c.age += 1} every year ;^>

···

On Thu, 01 May 2003 at 13:12 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:

I would like to go for [1] since it is very simple but in many cases it
just shifts the complexity to the client since the client now has to
buffer data in a structure that probably looks a lot like Contact until
it has enough to create a valid contact.
So I guess I should use [2] but I don’t know how to do that … do I
create two classes of objects: IncompleteContact and Contact. Generate
the second from the first through validation ?

How about trying both ways and seeing what happens: I suspect you’re
perhaps overthinking this problem…

FWIW, I think there are two forms of validity you need to consider:
object-level and persistence-level. Objects must always maintain their
class invariants, but these invariants can be laxer than those required
by the persistence layer. For example, persistence may insist on a
unique, non-blank name, while the object representation may not care.

For a while I had validation performed by my save method. However I
started finding I needed more flexibility: in particularly I often
needed to validate but not store. So now I have two methods. Here’s an
actual routine that captures details of sales parameters from a web page
and validates them. It saves the business object away if correct,
otherwise it reports the errors. (The @data stuff is my session
persistence interface, and the hash business lets me transfer fields
back and forth by name in a lightweight way)

def handle_maintain_sales
sp = @data.sp
values = hash_from_cgi
sp.from_hash(values)
errs = sp.error_list # this is the validation call, returning
if errs.empty? # an array of errors
sp.save
note “Parameters updated”
@session.pop
else
error_list(errs)
maintain_again
end
end

Cheers

Dave

in message news:gFdsa.70370$t_2.6650@afrodite.telenet-ops.be…

There is one thing that still puzzles me though: whether to allow for a
‘forbidden/incomplete’ object to exist at any point or not. Suppose I
decide that a Contact instance without a ‘Name’ is utterly pointless and
should not be entered in the addressbook at all. It is easy to have
contacts refuse inconsistent or forbidden data (like an empty name or a
wrong birth-date format) but how do I make sure all contacts that are
created are (or become) consistent ?

Given the way I am currently going with this design I see 2 ways to
implement this:

[1] Make it impossible to create an ‘incomplete’ contact by requiring
the name at construction (and making sure it cannot be set to nil or
“”
afterward).

[2] Allow clients to create an ‘incomplete’ (nameless) draft version of
the contact and have them call some #validate function that checks
the completeness of the contact and creates the definitive version.

The problem is very relevant - I have seen it many times.

First, it is worth being aware that there is the data, and the container.
The actual unique key is only relevant in context of the container (the
index).
Therefore this question has very much to do with implementation - how long
can you wait until requiring correct data.

There are a number of ways to look at your data that might affect the
decision.

As a Relational Database Record. In this case you can view the name as the
“Primary Key”. Policy here is that you general can modify any fields but the
point where you update your record, the database engine may complain about
the key not being unique. In the database you can also choose not to require
a unique primary key.

In the Contact database you certainly don’t want to have unique keys -
instead you want to list the prospects you user might be interested in after
a search. What you would do is to assign a new contact a new id, such as a
customer number. This id is dynamically created as needed so it is never an
issue to get it unique (actually it is an issue in distributed systems, but
never mind that). But for the purpose of a design excercise, lets continue
to assume we need a unique name.

In object models you can create a detached child object, fill it in with
data, and eventually add the object to a container in the object model.
Here there is no reason to require uniqueness before the time where the
object is appended.

Another way to deal with object models is to use the AddNew method. In this
case the child object (here contact) is both created and appended at the
same time. AddNew will then require a unique name and complain immediately
before you get a chance to add more data.

I generally do not like object models where you create detached objects that
are later appended. This gives all sorts of problems - typically an object
also has a reference to the root of the object model, or the “Document”.
While being detached there is no document and many operations work
differently. Another problem is issues with lifetime management and even
allocation although this is less relevant in garbage collected languages
unless the object references external resources such as files.

You can also choose AddNew, but allow an inconsistent definition until a
call to an Update method - this is also nice if you later want to change the
name.

A perfect example of this problem are files in a filesystem. Generally you
first use AddNew, as in create_file(filename) or fopen(filename).
In Unix a file can also exist anonymously without having a name, or it can
have multiple names. In relality the file is identified by a unique id - the
inode number. For the average user you don’t see the inode so appears like
an AddNew system.

I would like to go for [1] since it is very simple but in many cases it
just shifts the complexity to the client since the client now has to
buffer data in a structure that probably looks a lot like Contact until
it has enough to create a valid contact.
So I guess I should use [2] but I don’t know how to do that … do I
create two classes of objects: IncompleteContact and Contact. Generate
the second from the first through validation ?

For [2] you can use a factory method to create the object and a second
method to add the object to the system. This is the save operation suggested
by Dave.

But there isn’t really a right way to do it. It’s all about the language and
the object model you generally want. I guess you can look at REXML versus
DOM object model to see the variation in design of object models.

When possible I like to think of a unique key as something that does not
belong to an object but rather something that belongs to a relation between
the current object and a mapping object. This relation can be created,
removed and changed at any time (Here the mapping object would be
AddressBook or something inside it). Obviously search facilities will only
be available when the mapping relation exists but the object can easily
exist without it. This view makes it possible to let an object have any
number of values that kan be unique in some relations and non-unique in
other relations. You can in other words index you object in arbitrarily many
ways.
To deal with the life time issue you can have one ownership relation that
optionally maps a dynamically created unique id to the object (like the
inode).
If this unique id relation is broken, you should also break all other
mapping relations i.e. remove the unique name from the name index etc. and
eventually deallocate the resources tied to the object (such as memory in
non garbage collected systems).

Mikkel

···

“Simon Vandemoortele” deliriousREMOVEUPPERCASETEXTTOREPLY@atchoo.be wrote

Simon Vandemoortele wrote:

Wouldn’t I still need an object that keeps track of all the instantiated
contacts. How else am I going to generate a sorted list of contact names
for the UI ?
Surely all the Contacts that you search are the persisted ones: you
don’t want folks looking at possibly inconsistent reified ones.

Who is going to answer sophisticated search queries (give
me all contacts born between 1977 and 1987.) I could achieve this using
the ObjectsSpace in my client but is that really good design ?
Especially since many clients are probably going to want that query
functionality …

Why not Contact itself? For example, in my web application, class User
has class methods

User.user_with_name(name) → aUser
User.user_with_email(email) → aUser
User.list_matching(criteria) → Array[user…]

where ‘criteria’ is an object representing search criteria, tied
indirectly in to my persistence layer.

Anyway, a suggestion. Code your application. Code it many different
ways. And see what you think of each. In some versions make a point of
relaxing what you believe to be good design practices in favor of doing
what seems natural. In others, design the snot out of it. And in the
end, see which application feels ‘right.’

And please let us know.

Cheers

Dave

Simon Vandemoortele wrote:

Wouldn’t I still need an object that keeps track of all the instantiated
contacts. How else am I going to generate a sorted list of contact names
for the UI ?

Surely all the Contacts that you search are the persisted ones: you
don’t want folks looking at possibly inconsistent reified ones.

That’s where I don’t follow you: in my scheme contacts are not persisted
until the user closes the application. Therefore I do not have that
clean separation ‘consistent persisted contacts’/‘inconsistent reified
contacts’ and that’s a problem I am still trying to solve (see my
reply to MikkelFJ).

Who is going to answer sophisticated search queries (give
me all contacts born between 1977 and 1987.) I could achieve this using
the ObjectsSpace in my client but is that really good design ?
Especially since many clients are probably going to want that query
functionality …

Why not Contact itself? For example, in my web application, class User
has class methods

What you are in fact suggesting is that I put the responsibility that is
currently in an Addressbook instance, in the Contact class. I don’t know
what to think of that … why merge two different aspects in one class
(the contact management interface and the contact interface) ? then
again; the Addressbook class is strongly coupled to the Contact class
anyway.

Anyway, a suggestion. Code your application. Code it many different
ways. And see what you think of each. In some versions make a point of
relaxing what you believe to be good design practices in favor of doing
what seems natural. In others, design the snot out of it. And in the
end, see which application feels ‘right.’

That’s what I’m thinking too.

And please let us know.

Will do.

···

On Thu, 01 May 2003 at 19:38 GMT, Dave Thomas wrote:


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