In reply to @Jason D, and for the sake of @Nitax: I'm really skimming the surface, because while it's basically easy, it also can get complicated. There's no way I'm going to re-write it better than Martin Fowler either (certainly not in 10 minutes).
You first have to sort out the issue of only 1 object in memory that refers to a specific depot. We'll achieve that with something called a Repository. CustomerRepository
has a GetCustomer()
method, and the DepotRepository
has a GetDepot()
method. I'm going to wave my hands and pretend that just happens.
Second you need to need to write some tests that indicate how you want the code to work. I can't know that, but bear with me anyways.
// sample code for how we access customers and depots
Customer customer = Repositories.CustomerRepository.GetCustomer("Bob");
Depot depot = Repositories.DepotRepository.GetDepot("Texas SW 17");
Now the hard part here is: How do you want to model the relationship? In OO systems you don't really have to do anything. In C# I could just do the following.
Customers keep a list of the depots they are with
class Customer
{
public IList<Depot> Depots { get { return _depotList; } }
}
alternatively, Depots keep a list of the customers they are with
class Depot
{
public IList<Customer> Customers { get { return _customerList; } }
}
// * code is very brief to illustrate.
In it's most basic form, any number of Customers can refer to any number of Depots. m:n solved. References are cheap in OO.
Mind you, the problem we hit is that while the Customer can keep a list of references to all the depot's it cares about (first example), there's not an easy way for the Depot to enumerate all the Customers.
To get a list of all Customers for a Depot (first example) we have to write code that iterates over all customers and checks the customer.Depots property:
List<Customer> CustomersForDepot(Depot depot)
{
List<Customer> allCustomers = Repositories.CustomerRepository.AllCustomers();
List<Customer> customersForDepot = new List<Customer>();
foreach( Customer customer in allCustomers )
{
if( customer.Depots.Contains(depot) )
{
customersForDepot.Add(customer);
}
}
return customersForDepot;
}
If we were using Linq, we could write it as
var depotQuery = from o in allCustomers
where o.Depots.Contains(depot)
select o;
return query.ToList();
Have 10,000,000 Customers stored in a database? Ouch! You really don't want to have to load all 10,000,000 customers each time a Depot needs to determine its' customers. On the other hand, if you only have 10 Depots, a query loading all Depots once and a while isn't a big deal. You should always think about your data and your data access strategy.
We could have the list in both Customer
and Depot
. When we do that we have to be careful about the implementation. When adding or removing an association, we need to make the change to both lists at once. Otherwise we have customers thinking they are associated with a depot, but the depot doesn't know anything about the customer.
If we don't like that, and decide we don't really need to couple the objects so tightly. We can remove the explicit List's and introduce a third object that is just the relationship (and also include another repository).
class CustomerDepotAssociation
{
public Customer { get; }
public Depot { get; }
}
class CustomerDepotAssociationRepository
{
IList<Customer> GetCustomersFor(Depot depot) ...
IList<Depot> GetDepotsFor(Customer customer) ...
void Associate(Depot depot, Customer customer) ...
void DeAssociate(Depot depot, Customer customer) ...
}
It's yet another alternative. The repository for the association doesn't need to expose how it associates Customers to Depots (and by the way, from what I can tell, this is what @Jason D's code is attempting to do)
I might prefer the separate object in this instance because what we're saying is the association of Customer and Depot is an entity unto itself.
So go ahead and read some Domain Driven Design books, and also buy Martin Fowlers PoEAA (Patterns of Enterprise Application Architecture)