Для чего конкретно нужен интерфейс для шаблона хранилища?

Я полностью понимаю идею дизайна шаблона репозитория. Но зачем нам нужно реализовывать интерфейсный класс iDepository? Для чего конкретно это нужно? Сам класс репозитория работает без класса интерфейса.

Я думаю, что кто-то ответит мне, что это для отделения от бизнес-логики и логики данных. Но даже если нет класса интерфейса, разве логика данных не отделена от логики данных?

3 ответа

Это так, что вы можете внедрить двойной тест класса IRepository, когда вы тестируете модуль бизнес-уровня. Это имеет следующие преимущества:

  1. Это позволяет вам легко определить неудачные тесты как вызванные бизнес-уровнем, а не уровнем хранилища;
  2. Это делает ваши тесты уровня бизнес-логики быстрыми, так как они не зависят ни от доступа к данным, который имеет тенденцию быть медленным, ни от настройки структуры базы данных и тестовых данных, которая имеет тенденцию быть очень медленной.

Один способ внедрить тест удваивается, когда модульное тестирование - это Constructor Injection. Предположим, что ваш репозиторий имеет следующие методы:

void Add(Noun noun);
int NumberOfNouns();

И это код вашего бизнес-класса:

public class BusinessClass {

    private IRepository _repository;

    public BusinessClass(IRepository repository) {
        _repository = repository;
    }

    // optionally, you can make your default constructor create an instance
    // of your default repository
    public BusinessClass() {
        _repository = new Repository();
    }

    // method which will be tested 
    public AddNoun(string noun) {
        _repository.Add(new Noun(noun));
    }
}

Чтобы протестировать AddNoun без реального репозитория, вам нужно настроить двойной тест. Обычно вы делаете это с помощью фальшивой среды, такой как Moq, но я напишу фиктивный класс с нуля, чтобы проиллюстрировать эту концепцию.

public IRepository MockRepository : IRepository {
    private List<Noun> nouns = new List<Noun>();

    public void Add(Noun noun) {
        nouns.Add(noun);
    }

    public int NumberOfNouns() {
        return nouns.Count();
    }
}

Теперь одним из ваших тестов может быть это.

[Test]
public void AddingNounShouldIncreaseNounCountByOne() {
    // Arrange
    var mockRepository = new MockRepository();
    var businessClassToTest = new BusinessClass(mockRepository);

    // Act
    businessClassToTest.Add("cat");

    // Assert
    Assert.AreEqual(1, mockRepository.NumberOfNouns(), "Number of nouns in repository should have increased after calling AddNoun");

}

Это позволило вам протестировать функциональность вашего метода BusinessClass.AddNoun, не затрагивая базу данных. Это означает, что даже если есть проблема с вашим уровнем репозитория (скажем, проблема со строкой соединения), вы можете быть уверены, что ваш бизнес-уровень работает должным образом. Это охватывает пункт 1 выше.

Что касается пункта 2 выше, всякий раз, когда вы пишете тесты, которые тестируют базу данных, вы должны убедиться, что она находится в известном состоянии перед каждым тестом. Обычно это включает удаление всех данных в начале каждого теста и повторное добавление данных теста. Если этого не сделать, то вы не сможете запустить утверждения, скажем, против количества строк в таблице, потому что вы не будете уверены, что это должно быть.

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

Что касается использования абстрактных классов - да, это обеспечило бы такую ​​же возможность поставлять тестовые двойники. Я не уверен, какой код вы бы выбрали для абстрактной базы, а какую конкретную реализацию. Этот ответ на вопрос SO имеет интересную дискуссию об абстрактных классах и взаимодействиях.

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

Абстракции в.NET обычно представлены интерфейсами, так как никакая логика (код) не может быть присоединена к интерфейсу.

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

Интерфейс также позволяет вам развивать ваш уровень данных. Например, вы можете начать с использования базы данных для всех классов репозитория. Но позже вы захотите перенести некоторую логику за веб-сервис. Тогда вам нужно только заменить хранилище БД на хранилище WCF. Вы также можете обнаружить, что репозиторий работает медленно, и вы захотите внедрить в него просто кэш памяти (используя memcache или что-то еще)

Я нашел очень полезную страницу msdn, демонстрирующую идею разработки через репозиторий и тестирование. http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx

Другие вопросы по тегам