clinton has asked for the wisdom of the Perl Monks concerning the following question:

Hi all

I'm trying to figure out how to map my data requirements to objects. I have a typical parent/child database where (eg) a Site object would be the parent to all the User and Notice (family announcement) objects in that website:

Root: - Site : domain.com - User : joe@bloggs.com - User : jane@bloggs.com - Notice : 123 - Notice : 124 etc
That's fine, because each User and Notice object is unique in the DB. All objects which live in the database inherit from DBObject, which provides ORM functionality.

But now, I want to add Charities, classified by Category. So I have a Charity::Category object, which can be parent to other Charity::Category objects. But the charities themselves can appear in more than one category, so I have written a Set class, which sub-classes DBObject, to provide set membership.

Root: - Site : domain.com - Charity : Amnesty International - Category : International charities (members : Amnesty Intern +ational) - Category : Children's charities - Category : International (members : Amnesty International +) etc

All well and good. I can ask for Charity::Category->members and get all the Charities in that category

So now I'm thinking... Notices can have Images, Comments, Editors, and Charities. What is the best way to implement this? I see two options:

  1. Notice is parent to CharityList, ImageList, CommentList, EditorList, and each of these objects inherit from Set, and contain just one class of object (ie just Charities, or just Images):
    Notice: 123 - CharityList : 124 (members: Charity1, Charity2) - ImageList : 125 (members: Image1, Image2) etc

    so I could say :

    @charities = $notice->charity_list->members

    Disadvantage : I have to create a number of different classes, and I think it would be more DB intensive

  2. Notice itself sub-classes Set, and contains any class of object in its membership list:
    Notice: 123 (members: Charity1, Charity2, Image1, Image2) etc

    and I can say:

    @charities = $notice->members({type => 'charity'})

    Disadvantage : Sets cache their membership lists, and so I would need to cache different types of lists (eg {type => 'Charity'}, {type => 'Charity', status => 'active'}), etc, which would make it trickier.,

Which is the cleaner, more OO solution? (Have I explained my question sufficiently?)

thanks

Clint

Replies are listed 'Best First'.
Re: How to implement set-style membership lists in my obejcts
by jbert (Priest) on May 16, 2007 at 12:05 UTC
    It seems to me that you're trying to force the inheritance nature of things a bit too much.

    Ordinarily, sub-classing is an "is-a" relationship (a 'Dog' is-a 'Animal'). I wouldn't say that a User is-a Site. This is more properly a 'has-a' relationship. (A Site has Users). Other than that, Site and User are unrelated (neither inherits from the other).

    RDBMSs can happily and efficiently express has-a relationships, and there are ORM wrappers to allow you to do this in perl. I don't have a particular recommendation, but reading the docs for Class::DBI might give you a fresh perspective, and others might chime in with advice.

    So I'd say: "Don't try to force your data into a purely hierarchical system if it isn't, and don't try to model all hierarchical relationships as OO inheritance."

      I wouldn't say that a User is-a Site.

      Sorry, a misunderstanding, I wasn't saying that User subclasses Site, but rather that user:123 has the parent Site:1.

Re: How to implement set-style membership lists in my obejcts
by jettero (Monsignor) on May 16, 2007 at 12:50 UTC

    Moose might have things to help you. I find perl5's objects to be a little weak and Moose seems to make up for it a bit. I stumbled on Presto which uses DBM::Deep and Moose for intense coolness. That may be of interest as well. They all seem a bit too young (possibly except Moose) to use for anything serious, but it can't be long before they're good.

    (I believe the authors of most of those are regular monks, not that that really matters, but it's neat.)

    -Paul

      thanks jettero. I'm adding functionality to an existing system, rather than wanting to reimplement, but I'll check these out (along with DBIx::Class) for ideas
Re: How to implement set-style membership lists in my obejcts
by Moron (Curate) on May 16, 2007 at 14:05 UTC
    I know it's late in the lifecycle , but I fear you need to get your data modelling right first and then map the entities to objects after that. I would advise producing a Bachman diagram before and after the change (I'll enhance that wiki later today - it looks too bare). Here is some very old documentation but which reasonably illustrates the use of this type of diagram in data modelling http://h71000.www7.hp.com/doc/82final/6393/6393pro_020.html.

    Try to reduce your entities to one-to-many relationships, even where they are many-to-many in nature. That is done using a link table. Make sure all entities have primary keys and all child entities have a foreign key per relationship in which they are the many entity. Types (e.g. in your case notice type) are also a parent entity where the notice itself is the child. An entity maps to a concept like type, not just the objects in the real world. When your model is expressible as a connected network of one-to-many relationships then you are ready to translate it into objects. Hope this helps :)

    __________________________________________________________________________________

    ^M Free your mind!

      thanks moron

      For clarity, a Notice is eg a Wedding, Birth or Death announcement which appears in a newspaper. This is (an excerpt of) what my DB looks like:

      Table: object (generic object) ------------------------------ id type_id status parent_id creator_id created last_modified Table: notice (adds notice specific fields to the object table) --------------------------------------------------------------- id (1 to 1 relationship with object.id) notice_type (ie wedding, funeral, birth etc) title content etc Table: charity (adds charity specific fields to the object table) ----------------------------------------------------------------- id (1 to 1 relationship with object.id) name description url etc

      The editor of a notice can choose a number of charities to link to, so a notice can have many charities. This is stored in the members table:

      Table: members (link table) ---------------------------- set_id (in this example, the notice ID) member_id (in this example, the charity ID)

      Notices can have many Comments, Images, Charities, Editors. So my question really boiled down to, should I have

      • separate List/Set/Container objects for each list of items, so that you create a new ImageList, which is a child (not a subclass) of the Notice, and the members.set_id == ImageList.id
      • or set_id == notice.id, and then you filter the set members you want by object type (ie image/comment etc)

      After discussion in the CB with bart and castaway, I think i've abandoned both of those, and now what I'm going to do is the following:

      • A Set does not live in the database as an object, neither does Notice inherit from the Set
      • Instead, I can create a Set object as follows:
        $set = Set->new({ set_id => $notice->id, name => 'imagelist', # combined with set_id to provide + an ID for caching selectors => { type => 'image' }, });
        ... which is really just a glorified array, with methods to check membership, and to add / delete members from the list, almost like an SQL view.
        Technically your 1:1 are really 1 to many because 1:(0or1) is implemented in practice as a 1 to many inthat order. The set/notice is in theory a many-to-many which is more normally implemented as member -< member-notice >- notice with member-notice as the link table, so that the "set" is really a view on member-notice for a given notice.

        What I'd be doing about sets is making a container object "set" that requires an instance variable to have a particular notice filled in so it can go retrieve all member-notice pairs for that notice, thereby populating the container class.

        __________________________________________________________________________________

        ^M Free your mind!