Дизайн класса с N-уровневой архитектурой

Этот вопрос очень похож на вопрос, который я задал ранее, но я не думаю, что это одно и то же, поэтому подумал, что было бы разумно разделить их (что немного иронично, когда вы слышите вопрос).

Предыдущий вопрос

У меня есть веб-приложение, разделенное на два проекта:

1) Сайт ASP.NET 2) DLL, содержащая бизнес-объекты, методы логического уровня и методы DAL. Они разделены на разные пространства имен, но все в одной DLL.

Чем больше я смотрел на это и расширял разработку, тем больше понимал, что иду по ложному пути. Я хотел разделить их на отдельные библиотеки DLL, чтобы четко разделить обязанности.

Когда у меня был код в разных DLL, я понял, что многие классы называют друг друга. Например, когда в DAL был создан список объектов клиентов, он также вызвал бы companyDAL.cs и получил список компаний, к которым принадлежали клиенты. Итак, у меня теперь было два класса, которые напрямую ссылались друг на друга. Это кажется довольно плохим! Пахло плохо, но я продолжал и пытался разделить вещи, где мог.

Для этого я перенес все объекты в интерфейсы и изменил все ссылки на них в интерфейсе.

Я надеялся использовать какой-то DI (в данный момент просто узнал об этом), поэтому для каждого объекта я создал конструктор, который использовал Factory для этой DLL (эта фабрика создаст новый экземпляр каждого объекта, который класс может требовать). Я также поместил в него конструктор по умолчанию, который будет вызывать конструктор DI с фабрикой по умолчанию, чтобы гарантировать, что всегда используется одна фабрика.

Все было скомпилировано, поэтому я попробовал его и получил stackoverflow! Именно тогда я полностью осознал, что если у меня есть два класса, которые требуют друг друга, вы не можете создавать экземпляры без другого.

Factory
{
   IClient CreateNewIClient()
   {
         return new Client();
   }

   ICompany CreateNeIwCompany()
   {
          return new Company();
   }
}

Client
{
    private ICompany _company;

    public Client() : this (new Factory()) {}

    public Client(Factory factory)
    {
        _company = factory.CreateNewICompany();
    }

    public Client GetClientbyID(int id)
    {
       .... do stuff to get client from db
       ... got client object and companyid from db.
         client.Company = _company.GetCompanybyID(companyid);
        return client;
    }
}

 Company
 {
    private IClient _client;

    public Company() : this (new Factory()) {}

    public Company(Factory factory)
    {
        _client = factory.CreateNewIClient();
     }

    public Company GetCompanyWithAllClients(int companyid)
    {
         .... do stuff to get a company out and client ids
         .... for each client id found
          company.Clients.add(_client.GetClientByID(clientid));
          return company;
    } 
 } 
  • Я что-то не так говорю с DI, это заводская идея в порядке?

  • Как мне избежать занятий, которые нужны друг другу? Причина, по которой они такие сейчас, заключается в том, чтобы избежать повторения кода. Я уверен, что мог бы написать более чистый SQL, чтобы получить все за один раз или, по крайней мере, за пару запросов одним и тем же методом, но я пытался избежать повторения кода в разных местах.

Спасибо за любые предложения.


person Jon    schedule 18.04.2009    source источник


Ответы (2)


Это не столько ответ, сколько прояснение проблемы. Я думаю, что ваша фабрика несколько усложняет ситуацию (в том смысле, что вы получаете исключение переполнения стека, а не вообще не можете скомпилировать).

Проблема в круговой зависимости. Вы не можете создать экземпляр A без экземпляра B, и вы не можете создать экземпляр B без экземпляра A.

public class Company : ICompany
{
    private IClient _client;

    // OK so first build a client and pass it in
    public Company(IClient client)
    {
       _client = _client;
    }
}

public class Client : IClient
{
    private ICompany _company;

    // OK so first build a company and pass it in.  Oh.  I can't... :(
    public Client(ICompany company)
    {
        _company = company;
    }    
}

У Миско есть статья об этом, которая может быть полезна: http://misko.hevery.com/2008/08/01/circular-dependency-in-constructors-and-dependency-injection/

По сути: дизайн вашего класса - это проблема, и ее нужно исправить. Завод просто запутывает вопрос :)

person Mark Simpson    schedule 18.04.2009
comment
Марк, вы попали в самую точку. Вы бы порекомендовали иногда использовать повторяющийся код, т.е. некоторые из ваших операторов sql могут делать очень похожие вещи? Что касается этой ситуации на уровне бизнес-логики и объекту компании, которому требуется экземпляр клиентского объекта, но, зная только идентификатор, должен ли он вызывать объект уровня Client DAL? - person Jon; 18.04.2009

Может быть, вы могли бы использовать какой-нибудь инструмент ORM для DAL, например NHibernate или Linq to SQL? NHibernate отобразит ваши данные в объекты с двунаправленными ссылками (если хотите).

Изменить: опечатка

Ура, Рок

person rrejc    schedule 18.04.2009
comment
NHibernate - это то, на что я хочу взглянуть, но я думаю, что для меня важно понять, как все должно быть собрано, прежде чем я буду использовать инструменты для автоматизации чего-либо из этого, иначе я беспокоюсь, что просто скрою некоторые из своих проблем. - person Jon; 18.04.2009