Наследование и композиция — рассмотрение архитектуры ООП

У меня возникли проблемы с поиском чистого способа реализации моих слоев.

Вот слои, которые у меня есть (нижние слои поддерживают верхние слои либо через наследование, либо через композицию):

Business Logic Layer (BLL)
Datastore Layer (DSL)
Database Layer (DBL), Web-Service Buffer (WSB)

Когда объект BLL делает запрос на получение, getRecords(), он, в свою очередь, просит DSL выполнить его.

Затем DSL решает, использовать ли локальный DBL или использовать WSB (который связывается с удаленным интерфейсом веб-службы с «главной» БД).


Вариант 1 - Композиция (BLL имеет DSL, DSL имеет DBL)


Моя проблема заключается в том, что, поскольку объекты DSL и DBL составлены внутри BLL, они ничего не знают о содержащемся BLL, и поэтому как они должны делать определенные запросы к БД, которые включают поля BLL?


Вариант 2 — Наследование (BLL: DSL, DSL: DBL)


Даже если нижние уровни теперь имеют доступ к BLL через общедоступное/защищенное наследование, все еще остается вопрос, как DSL и DBL должны точно знать, какие конкретные строки запроса генерировать. Я предполагаю, что BLL может хранить набор статических строк, но тогда это создает двустороннюю зависимость, что, как я полагаю, является действительно ошибочным дизайном с ужасными последствиями.

Примечание. У каждого BLL есть соответствующая таблица, в которую он сериализуется/десериализуется.

Примечание. Я не хочу использовать рефлексию и хотел бы ограничить использование дженериков (если только нет другого пути. Я использую дженерики для своего WSB, который отлично работает, хотя моя основная задача заключается в создании строк запроса, специфичных для BLL, в слоях DSL и DBL).

Примечание. Будет много различных объектов BLL, которые будут использовать слои DSL и DBL, а не только один (иначе это было бы тривиально).

public class BL1 
{
  private DSL _dsLayer;

  public getRecords() 
  {
    // ...
    _dsLayer.getRecords();
    // ...
  }
}

public class DSL 
{
  private DBL _dbLayer;
  private WSB _wsBuffer;

  public getRecords() 
  {
    if(_dbLayer.getRecords() != null)
    {
      return records;
    }
    else
    {
      return _wsBuffer.getRecords();
    }
  }
}

public class DBL
{
  private string _db = "file.db3";

  public getRecords()
  {
    select ????? - how to know what fields to grab
  }
}

Спасибо, что нашли время ответить на этот вопрос, это очень ценно.


person samis    schedule 17.07.2012    source источник
comment
Полезно прочитать: Основы секционирования и многоуровневости google .com/   -  person samis    schedule 24.07.2012


Ответы (3)


В общем, наследование следует использовать, когда у вас есть отношение «Является ли А». Поскольку вы не можете сказать BLL 'Is-A' DSL или DSL 'Is-A' DBL, я бы предпочел композицию наследованию. Побочным эффектом этого является упрощение тестирования каждого логического уровня, поскольку вы можете заглушить или имитировать каждую зависимость.

Как правило, в любом открытом API серверная сторона (то есть DSL с точки зрения BLL -> DSL) должна предоставлять объекты для выполнения своей работы. Вы правы, указывая, что DSL не должен знать об объектах BLL. Таким образом, задача состоит в том, чтобы написать чистый API для DSL, который будет доступен BLL для запроса данных.

Я бы посоветовал посмотреть как репозиторий, так и Спецификация шаблонов из DDD. Они помогают решить некоторые проблемы, которые вы поднимаете.

person Davin Tryon    schedule 17.07.2012
comment
Очень полезная информация, и я люблю обобщения, так что спасибо за ссылки (у меня тоже есть книга Фаулера, так что я обязательно проверю там и шаблон репозитория). - person samis; 17.07.2012
comment
Вопрос: при написании API все методы обычно статичны или обычно используется переменная экземпляра (то есть будет ли явный член DSL в BLL или просто вызовы статического API DSL? - person samis; 17.07.2012
comment
instance vs static на самом деле не связаны, ИМХО. Подумайте обо всех замечательных API-интерфейсах .NET, которые существуют в фреймворке. Многие из них требуют, чтобы вы создали новый экземпляр объекта перед его использованием. Как правило, я избегаю статики для композиции, потому что это усложняет тестирование (потому что требует более тесной связи между компонентами). В качестве предложения вам может быть интересно посмотреть на инъекцию зависимостей. - person Davin Tryon; 18.07.2012

Вы должны пойти с Композицией. Я не думаю, что наследование имеет смысл, потому что это совершенно разные типы объектов, и на самом деле один не может наследовать от другого. Какие поля они наследуют? И кто от кого наследует?

BLL должен будет передать список «полей», чтобы добраться до DSL. Либо как параметры методов DSL, либо как-то иначе. Методы DSL просто получают некоторый список полей в качестве параметров и работают с ними. Я думаю, что это осуществимое решение.

Кроме того, вы должны создавать интерфейсы на каждом уровне и программировать их вместо использования самого типа. Так, например, в написанном вами образце кода измените DBL и WSB на IDBL и IWSB. Это поможет вам лучше тестировать и допустить слабую связь в коде.

public class DSL 
{
  private IDBL _dbLayer;
  private IWSB _wsBuffer;
....

}
person Punit Vora    schedule 17.07.2012
comment
Да, я думаю, вы правы в том, что аргумент списка полей передается DSL. По какой-то причине я думал, что это создаст зависимость, но это просто аргумент функции, как и любой другой, верно? Спасибо desigeek. - person samis; 17.07.2012
comment
Передача аргументов не является причиной или источником зависимости. Класс, метод которого вы будете использовать, будет зависимостью для вашего класса контейнера (поэтому DSL является зависимостью от BLL и т. д.). Чтобы отделить эту зависимость, вы должны создать интерфейс, получить от него зависимый класс и объявить соответствующий член вашего вызывающего класса как интерфейс вместо самого зависимого типа. что-то вроде IDBL и IWSB в ответ. Это один из основных принципов принципа «внедрения зависимостей». - person Punit Vora; 17.07.2012
comment
Вы хотите, чтобы BLL реализовал IDBL (BLL: IDBL)? Если да, то не придется ли мне тогда писать конкретные реализации метода IDBL для каждого объекта BLL (которых у меня будет довольно много, не менее 10)? - person samis; 17.07.2012
comment
... эээ, не обращайте внимания на особенности последних вопросов, однако концепция остается. Я хочу написать DSL/DBL только один раз, и если задействованы интерфейсы, не придется ли мне реализовывать методы интерфейса для каждого типа BLL (которых много)? - person samis; 17.07.2012
comment
Кажется, я знаю, о чем ты говоришь: public class DBL : IDBL {...} public class DSL { private IDBL _dbl; } - person samis; 17.07.2012
comment
Ага. ваш последний комментарий, что я хотел сказать. - person Punit Vora; 17.07.2012
comment
Спасибо. После прочтения lostechies.com/jimmybogard/2009/09/03 / , вопрос, который я пытался задать в своих последних комментариях, в основном касается того, хочу ли я реализовать Универсальный интерфейс репозитория или Универсальный репозиторий методов. . Просто надо решить. - person samis; 17.07.2012
comment
Теперь я вижу, что ваше предложение выше является фундаментальной концепцией в DDD. Я не осознавал его значимости, пока не увидел полную картину. Спасибо, очень хороший совет, которым я почти наверняка воспользуюсь. - person samis; 17.07.2012
comment
... На самом деле, по словам Грега Янга (codebetter.com /gregyoung/2009/01/16/ddd-the-generic-repository) - Таким образом, ответ здесь заключается в том, чтобы по-прежнему использовать общий репозиторий, но использовать композицию вместо наследования и не предоставлять его домену. в качестве контракта. Я буду держаться подальше от интерфейсных контрактов и использовать композицию для предоставления функциональности хранилищу данных. - person samis; 17.07.2012
comment
desigeek: после прочтения «Основы разметки и слоев» от Microsoft я понял, что вы предлагаете выше. В статье это называется композицией интерфейса (композиция интерфейсов, а не конкретных объектов). Буду пользоваться этой техникой, спасибо. - person samis; 24.07.2012

Сочинение. Из-за всего того, что сказали dtryon и desigeek, а также
из-за того, что в вашем случае Наследование выглядит неестественно + оно сделает все ваши
слои тесно связанными и вряд ли ограничить внесение любых изменений в исходный код.

Я считаю, что было бы полезно взглянуть на prefer-composition-over-inheritance SO- тема.

person alex.b    schedule 17.07.2012
comment
Спасибо, и ссылка тоже отличная. Хорошая вещь. - person samis; 17.07.2012