-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: Any good patterns for association management?
PostPosted: Fri Aug 11, 2006 1:35 pm 
Beginner
Beginner

Joined: Wed Dec 28, 2005 3:14 pm
Posts: 29
The question has been haunting me for month. I beleive it's a general one for anyone who wants to expose only interface rather than classes to outside the domain layer.
Say we have two interfaces Foo and Bar, there is a one-to-many bidirectional association between them.
Code:
public interface Bar{
   Foo getFoo();
}

public interface Foo{
   ISet<Bar> getBars();  //returns a readonly Set
   void add(Bar b);
   void remove(Bar b);
}

The problem is related to the management of the association. As you can see from the interfaces, we decided to let Foo to management this one-to-many association while Bar does nto have the right to change the association.
The question is how the implementation of the Foo manage the association at the Bar side?
Take the following implementation as an example:
Code:
class FooImpl implements Foo {
private ISet<Bar> bars;
public ISet<Bar> getBars(){
    return Set<Bar>.readonly(bars);
}
public void add(Bar b){
     bars.add(b);
    //?????? how to set the bar of f to be this?
   }
}


If we don't want to add any method to the Bar interface, one way to do this is write such a BarImpl:

Code:
class BarImpl implements Bar{
   private Foo foo;
   public Foo getFoo(){
     return foo;
  }
   void setFoo(Foo f){
       foo =  f;
  }
}

Now in the FooImpl we can downcast the inputed b to BarImpl and call the setFoo function
Code:
    public void add(Bar b){
       bars.add(b);
       ((BarImpl)b).setFoo(this);
   }


As we all can see, this solution is probably not accetable because 1)downcast couple the implementation of two and 2) more importantly, downcast might failed when proxy is used in the Nhibernate envrionment.
The only way that comes up to my mind to avoid this downcast is to add a package private interface, and use this interface between the implementations of Foo and Bar
Code:
interface HasFoo{
   void setFoo(Foo f);
}


class BarImpl implements Bar, HasFoo{
   ...
   void setFoo(Foo f){
       foo =  f;
  }
}

class FooImpl implements Foo{
   ...
   public void add(Bar b){
       if (b instanceof HasFoo){
         ((HasFoo)b).setFoo(this);
          bars.add(b);
       } else
            throw new Exception() 
    }
}



I don't think this solution is perfect since it introduced a rather "useless" interface. Does anyone know a good pattern for this?


Last edited by alfbolide on Sat Aug 12, 2006 12:14 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 11, 2006 3:54 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Could you please reiterate why you do not want to add a setBar(Bar bar) method in the Foo interface? Not being a smart alek, it is an interesting problem you bring up, just would like to know the justification for this restriction. I do pretty much the same thing as you, exposing only the interfaces, but always expose the setters for the * end of the 1..* relationship to do exactly this kind of bidirectional association.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 11, 2006 3:59 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
By the way, it took me almost 5 minutes to figure out the association. For some reason, whenever I see a Foo/Bar example of a 1..* relationship described, I always assume that it is the Foo that has many Bars and not v.v. Is that just my own personal quirk or did anyone else have that problem? (Probably just me, it's late in the day and I've been working on Freemarker code for hours).


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 11, 2006 4:53 pm 
Newbie

Joined: Thu Jul 20, 2006 5:00 pm
Posts: 16
I agree with you esp. when trying to understand which has one or which has many. It should be self explanatory. Like Book and Chapters. employer and employees. Its good to put the question using the proper example ... it happaned some time that I use to leave the topic in the middle, if requester does not use proper example.

Any way have a great weekend.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 12, 2006 12:19 pm 
Beginner
Beginner

Joined: Wed Dec 28, 2005 3:14 pm
Posts: 29
Ananasi wrote:
By the way, it took me almost 5 minutes to figure out the association. For some reason, whenever I see a Foo/Bar example of a 1..* relationship described, I always assume that it is the Foo that has many Bars and not v.v. Is that just my own personal quirk or did anyone else have that problem? (Probably just me, it's late in the day and I've been working on Freemarker code for hours).

Thanks Xgrs. I am actually not quite familar with the Foo Bar thing. I've correct the post by exchange the class names. Now it's the Foo that has many Bars.
The reason for not exposing the setFoo method to the interface is that I don't want to allow the client code to be able to call this method since calling this method alone will destroy the data intergity in thie Foo and Bar model.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 14, 2006 10:35 am 
Beginner
Beginner

Joined: Wed Dec 28, 2005 3:14 pm
Posts: 29
hello


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 14, 2006 10:44 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Still thinking about it, haven't forgotten you. You've been thinking about it for a month, at least give me a day or two! ;)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 14, 2006 11:47 am 
Beginner
Beginner

Joined: Wed Dec 28, 2005 3:14 pm
Posts: 29
Thanks Ananasi! An elegant solution to this will make the world perfect to me. :)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 14, 2006 4:17 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Possible solutions so far:
  1. Refactor for a package access setFoo() (I'm sure you already looked at this one)
  2. Use inner class keys to restrict access to the setFoo() method. Kind of an ugly hack to emulate C++ friend access mechanism. See example below.
  3. Wait for JSR277 (scheduled for J2SE7, that's a long time to wait :D ).
  4. Use an intermediary class to access package scoped methods/fields and mark the class as deprecated to alert developers to the perils of using said class. I assume that this is not an acceptable solution for complete restriction to the aforementioned setter, as it only targets developers who would read the javadoc and pay attention to the deprecated directive.

Example for 2:
Code:
public interface Bar {
   Foo getFoo();
   void setFoo(Foo foo, Object key);
}

class BarImpl implements Bar {
   private Foo foo;
   public Foo getFoo(){
     return foo;
  }
  void setFoo(Foo f, Object key) throws AnException {
    if (key.getClass().getName().equals("FooImpl$Key") == false) {
      throw new AnException();
    }
    foo =  f;
  }
}

class FooImpl implements Foo {
  static private final class Key {
  }

  private ISet<Bar> bars;
  public ISet<Bar> getBars(){
    return Set<Bar>.readonly(bars);
  }

  public void add(Bar b){
    bars.add(b);
    b.setFoo(this);
  }
}


In this example, other classes can call the setFoo method only through other introspection hacks. Of course, this makes the compiler/IDE useless for detecting this particular scoping construct, as it will only throw an error at runtime. But this will lock the method down. Maybe something like this along with detailed javadocs would help? Of course, you can scope the Key class differently to obtain different granular level scoping.

I'm still pondering other alternatives.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 14, 2006 5:46 pm 
Beginner
Beginner

Joined: Wed Dec 28, 2005 3:14 pm
Posts: 29
Thanks very much for your answer Ananasi.
I was thinking maybe it is reasonable to have two interface for Bar. A Bar for public and a HasFoo for internal. I don't like the idea of having an extra "HasFoo" interface there but now I think there might be a point for it to be there. I mean it's not just a hack caused by language limitition. As a class, Bar DOES have two interfaces (not a language concept here) towards difference types of clients: outside public client and internal clients that are more closely coupled.....
I tried using the internal interface, and found out that it's not uncommon that there are some other internal methods you want to add into it that you don't want public to have access. There is some cost but the benefit seems promosing. I'll keep trying and keep you posted about my new weird thoughts.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.