views:

69

answers:

3

I'm struggling to come up with a good way of adding a bidirectional relation in OO model. Let's say there is a Customer who can place many Orders, that is to say there is a one-to-many association between Customer and Order classes that need to be traversable in both directions: for a particular customer it should be possible to tell all orders they have placed, for an order it should be possible to tell the customer.

Here is a snippet of Java code, although the question is largely language-agnostic:

class Customer {
 private Set orders = new HashSet<Order> ();

        public void placeOrder (Order o) {
     orders.add(o);
            o.setCustomer(this);
 }

}

class Order {
 private Customer customer;
        public void setCustomer (Customer c) {
  customer = c;
 }
}

What buggers me is that given the model someone could easily call:

o.setCustomer(c);

instead of correct

c.placeOrder(o);

forming unidirectional link instead of bidirectional one.

Still learning OOP, could anyone please help with what would be an idiomatic and practical way of solving this problem without resorting to "reflection" or fancy frameworks (that would anyway rely on reflection). Any help is appreciated!

P.S. There is a similar question: Managing bidirectional associations in my java model, however I don't feel it answers my plea.

P.S.S. Any links to source code of real-life projects implementing business model on top of db4o are greatly appreciated!

+3  A: 

first, unless you plan on moving orders between customers, I think you shouldn't provide a setCustomer() method, the customer should be a parameter for the constructor and leave it unchanged.

then, the constructor shouldn't be accessible for the user, only use the factory method of Owner.

Javier
+1. For most use cases, this is the safest solution (though not always the easiest). If you are using hibernate ORM, you can make the setter method private, or use @AccessType(value="field")
RMorrisey
Javier, to hide Order constructor from everyone but Customer I'd need to make Order an inner class of Customer and define public Order interface right? It might work if Customer was the only source of Order objects.
Steve Furrow
The other thing, what if I needed a many-to-many bidirectional link, say: Question and Tag? When I wouldn't be able to do the constructor trick, since I should be able to add more associations as needed to existing Question and Tag objects.
Steve Furrow
A: 

There is no single answer. It really depends on the classes involved. In your case, you obviously don't want to give people the option of doing something invalid so I would get rid of Order.SetCustomer.

That may not always be the case though. Like I said, it depends on the classes involved.

Justin Niessner
Justin, if I got rid of Order.setCustomer how would Customer object signal to the Order object to add the other end of the association?
Steve Furrow
A: 

If you are maintaining the bidirectional relationship in Customer.placeOrder(Order), why don't you do the same thing in Order.setCustomer(Customer)?

class Order {
    private Customer customer;
    public void setCustomer (Customer c) {
        customer = c;
        c.getOrders().add(this);
        // ... or Customer.placeOrder(this)
    }
}

It seems like duplicating code but it solves the problem. The simpler thing to do though is to avoid bidirectional relationships where possible.

matt b