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.

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?  ;-)  and therefore on
topic!

-a


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