Примечание. Приведенные ниже примеры относятся к C #, но эта проблема не должна относиться конкретно к какому-либо языку.
Итак, я создаю объектную область, используя вариант S # Architecture . Для тех, кто не знаком с ним, и чтобы сэкономить ваше время на чтение, идея заключается в том, что у вас есть интерфейс доступа к данным для каждого из ваших доменных объектов, который отвечает за загрузку в / из слоя постоянства. Все, что может понадобиться для загрузки / сохранения данного объекта, принимает интерфейс доступа к данным этого объекта как зависимость. Так, например, у нас может быть следующее, где продукт будет лениво загружать клиента, который приобрел его по мере необходимости:
public class Product {
private ICustomerDao _customerDao;
private Customer _customer;
public Product(ICustomerDao customerDao) {_customerDao = customerDao;}
public int ProductId {get; set;}
public int CustomerId {get; set;}
public Customer Customer {
get{
if(_customer == null) _customer = _customerDao.GetById(CustomerId);
return _customer;
}
}
public interface ICustomerDao {
public Customer GetById(int id);
}
Это все хорошо, пока вы не достигнете ситуации, когда два объекта должны иметь возможность загружать друг друга. Например, отношение «многие к одному», где, как указано выше, продукт должен иметь возможность лениво загружать своего клиента, но также и клиент должен иметь возможность получить список своих продуктов.
public class Customer {
private IProductDao _productDao;
private Product[] _products;
public Customer(IProductDao productDao) {_productDao = productDao;}
public int CustomerId {get; set;}
public Product[] Products {
get{
if(_products == null) _products = _productDao. GetAllForCustomer(this);
return _products;
}
}
public interface IProductDao {
public Product[] GetAllForCustomer(Customer customer);
}
Я знаю, что это действительно распространенная ситуация, но я относительно новичок в этом. Моим камнем преткновения является то, что нужно делать при реализации объектов доступа к данным. Поскольку у Клиента есть зависимость от IProductDao, реализация CustomerDao также должна, однако обратное также верно, и ProductDao должен зависеть от ICustomerDao.
public class CustomerDao : ICustomerDao {
private IProductDao _productDao;
public CustomerDao(IProductDao productDao) {_productDao = productDao;}
public Customer GetById(int id) {
Customer c = new Customer(_customerDao);
// Query the database and fill out CustomerId
return c;
}
}
public class ProductDao : IProductDao {
private ICustomerDao _customerDao;
public ProductDao (ICustomerDao customerDao) {_customerDao = customerDao;}
public Product[] GetAllForCustomer(Customer customer) {
// you get the idea
}
}
И здесь у нас проблема. Вы не можете создать экземпляр CustomerDao без IProductDao и наоборот. Моя инверсия контейнера управления (Замок Виндзор) поражает круговую зависимость и задыхается.
Я придумала решение на время, которое предполагает ленивую загрузку самих объектов DAO (я опубликую это как ответ), но мне это не нравится. Каковы проверенные временем решения этой проблемы?
РЕДАКТИРОВАТЬ: Выше приведено упрощение архитектуры, которую я на самом деле использую, и я не рекомендую кому-то фактически передавать DAO для объекта. Лучшая реализация, более близкая к тому, что я на самом деле делаю, похожа на работу NHibernate, когда фактические объекты очень просты, а вышеприведенные являются прокси-объектами, которые наследуют и переопределяют соответствующие поля.