Reputation: 1090
I have a class A maintaining a list of objects class B. But each object of class B can be referenced in any object of class A. Class B also maintains a list of objects of class A where it is being referenced. The program can and will create (several) objects of both class A and B 'at will' and also delete them.
If I use C# I can add and delete objects from both classes with following code
public class A
{
private List<B>ListOfObjects_B;
public bool Add(B Object)
{
bool bAdd = false;
if ((Object != null) && (ListOfObjects_B.IndexOf(B) <0))
{
ListOfObjects_B.Add(Object);
Object.Add(this);
bAdded = true;
}
return bAdded;
}
public bool Delete(B Object)
{
bool bDeleted = ListOfObjects_B.Remove(Object);
if (bDeleted == true) Object.Delete(this);
return bDeleted;
}
}
public class B
{
private List<A>ListOfObjects_A;
public bool Add(A Object)
{
bool bAdd = false;
if ((Object != null) && (ListOfObjects_A.IndexOf(A) <0))
{
ListOfObjects_A.Add(Object);
Object.Add(this);
bAdded = true;
}
return bAdded;
}
public bool Delete(A Object)
{
bool bDeleted = ListOfObjects_A.Remove(Object);
if (bDeleted == true) Object.Delete(this);
return bDeleted;
}
}
This will work as because of removing/adding the object to the ListOfObjects the SECOND time (by recursion) the function will be called it will fail to delete/add thereby avoiding an infinite loop.
But I don't like this code even though A and B do not know 'much' about the other class and just call a Delete/Add function.
I suppose this kind of problem is general and a design pattern exists for handling it in such a way that recursion can be avoided and updating both lists will be 'just better'. What design pattern should I use? I would appreciate if some code would be added as well.
Upvotes: 4
Views: 375
Reputation: 292345
I recently wrote a class to handle a similar problem. It is actually a simpler scenario (parent/child relationship with the children referencing their parent), but you could probably adapt it for your needs. The main difference is that the Parent
property in my implementation should be replaced by a collection of parents.
Upvotes: 2
Reputation: 3577
About the only thing I can think of is using a Mediator pattern so that A doesn't add itself to B. Here's an example:
public class Mediator {
public void Add(A List, B Object) {
if(list.Add(Object)) {
object.Add(List);
}
}
public void Delete(A List, B Object) {
if(List.Delete(Object)) {
Object.Delete(List);
}
}
}
After this you would remove the lines of code which read "Object.Add(this);" and "if (bDeleted == true) Object.Delete(this);" This also has the benefit of reducing how many times each method is called as before object A's methods were being called twice since object B was also calling those methods on object A.
EDIT: Upon further review, I realized you were already using an Observer design pattern in a way. Object A is the observer and object B is the observable. Object A maintains a list of objects its observing and object B maintains a list of objects observing it. The only thing is I don't see any additional functionality, although there probably is some. Basically, Object B will notify all Object A's observing it that it's changed and all of those Object A's will ask for the change. If this is what you're looking for, then all you need to do is remove the lines "Object.Add(this);" and "if(bDeleted == true) Object.Delete(this);" from the B code as it is unnecessary.
Upvotes: 1
Reputation: 30723
You can simplify thing by moving the "object association concern" into a dedicated class. Here's what I have in mind.
Define a class called AssociationTable. This class will maintain a list of pairs where each pair holds a reference to an A object and a reference to a B object.
Each A object (and each B object) will hold a reference to the AssociationTable object. A.Add(B) will be implemented as table.add(this, b); B.Add(A) will be implemented as table.add(a, this);
Deletion will be implemented as table.delete(this, b) or table.delete(a, this)
class Pair {
A a; B b;
Pair(A a, B b) { this.a = a; this.b = b; }
// Also override Equals(), HashCode()
}
class AssociationTalbe {
Set<Pair> pairs = ...;
void add(A a, B b) { pairs.add(new Pair(a, b)); }
void remove(A a, B b) { pairs.remove(new Pair(a, b)); }
}
class A {
AssociationTable table;
public A(AssociationTable t) { table = t; }
void add(B b) { table.add(this, b); }
void remove(B b) { table.remove(this, b); }
}
Edit: The problem with this design is garbage collection. the table will hold references to objects thereby supressing their collection. In Java you could use a WeakReference object to overcome this issue. I am pretty sure there's something similar in the .Net world
Also, the table could be a singleton. I don't like singletons too much. In here, a singleton will make the A-B association unique across your program. This may be something that is undesirable but it depends on your concrete needs.
Finally, (just to put things in context) this design works the same way as Many-to-Many relationships in relational data bases.
Upvotes: 4