2015-05-06 6 views
11

В нашем C# MVC-приложении у нас есть много интерфейсов, которые отображают 1 к 1 с объектами, которые их реализуют. т.е.: в основном для каждого созданного объекта была выполнена операция «интерфейс экстракта».Принцип повторной абстракции в C#

Интерфейсы используются Moq для генерации макетных объектов для наших модульных тестов. Но это единственный раз, когда интерфейсы повторно используются.

В нашей системе нет конкретных объектов, реализующих несколько интерфейсов.

Может ли кто-нибудь сказать мне, если это вызовет проблемы в будущем? И если да, то каковы они будут?

Я думал, в нашем приложении много дублирования, например, в этих двух интерфейсах (Edit: на нашем уровне сервиса) единственное, что отличается, это имя метода и тип параметра, который они принимают, но семантически они делают то же самое с хранилищами они посылают сообщения:

interface ICustomer 
{ 
    void AddCustomer(Customer toAdd); 
    void UpdateCustomer(Customer toUpdate); 
    Customer GetById(int customerId); 
} 

interface IEmployee 
{ 
    void AddEmployee(Employee toBeAdded); 
    void UpdateEmployee(Employee toUpdate); 
    Employee GetById(int employeeId);  
} 

и где я думаю, что повторно принцип абстракции будет входить, т.е. преобразовать код что-то вроде:

public interface IEmployee: IAdd<Employee>, IUpdate<Employee>, IFinder<Employee> 

Речь идет не о патче репозитория tern - это о интерфейсах в любом слое, которые выглядят так, как будто они разделяют семантически идентичные поведения. Стоит ли вызывать общие интерфейсы для этих операций и наследовать от них «под-интерфейсы»?

По крайней мере, это обеспечило бы согласованность подходов методов. Но какие другие преимущества мне это дадут? (Принцип замещения Лискова в сторону)

В настоящее время имена методов и типов возврата повсюду.

Я прочитал блог Марка Сееманна о повторных абстракциях Принцип, но я не понимал этого, чтобы быть откровенным. Может быть, я просто глуп :) Я также прочитал определение Фаулера «Интерфейсы заголовков».

+0

Похоже, вам нужен общий репозиторий. –

+0

Мне придется искать повторно используемые абстракции, но ваше предложение выглядит как Принцип разделения сегрегации, который является частью кода SOLID. Это не может быть плохо :) – jrahhali

+0

Эти методы, которые я упомянул, на самом деле являются частью уровня сервиса (бизнеса). Мы не используем DDD. В нашей реализации контроллеры MVC используют «службы» (объекты, названные в соответствии с строками EmployeeServices, CustomerServices), которые реализуют интерфейсы, подобные приведенным выше. Они «используют» репозитории на другом уровне, реализованные в Entity Framework, которые имеют довольно похожие интерфейсы. – Scott

ответ

10

Все, что можно объединить с помощью Repository pattern ...

public interface IRepository<TEntity> where TEntity : IEntity 
{ 
    T FindById(string Id); 
    T Create(T t); 
    bool Update(T t); 
    bool Delete(T t); 
} 

public interface IEntity 
{ 
    string Id { get; set; } 
} 

EDIT

Никаких конкретных объектов в нашей системе не реализовать несколько интерфейсов.

Может ли кто-нибудь сказать мне, если это вызовет проблемы в будущем? И если да, какими они были бы?

Да, это вызовет проблемы, если он еще не начал это делать.

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

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

Вы подвели это,

Это не о хранилище шаблона - это об интерфейсах в любом слое, которые выглядят как они имеют семантически одинаковые модели поведения. Стоит ли вызывать общие интерфейсы для этих операций и наследовать от них «под-интерфейсы»?

Это не о interfaces, речь идет о abstractions, то Repository pattern демонстрирует, как можно абстрагироваться от поведения, которое с учетом конкретного объекта.

Пример, приведенный выше, не имеет методов с именем AddEmployee или UpdateEmployee ... такими методами являются только мелкие интерфейсы, а не абстракции.

Концепция Repository pattern очевидна тем, что она определяет набор поведений, которые реализуются рядом различных классов, каждый из которых предназначен для определенного объекта.

Учитывая, что репозиторий реализуется для каждого объекта (UserRepository, BlogRepository и т. Д.) И учитывая, что каждый репозиторий должен поддерживать основной набор функциональных возможностей (основные операции CRUD), мы можем взять этот основной набор функций и определить его в интерфейсе, а затем реализовать этот интерфейс в каждом репозитории.

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

public interface IVehicleOperator<TVehicle> where TVehicle : IVehicle 
{ 
    void Accelerate(); 
    void Brake(); 
} 

При этом у нас больше нет отображений 1: 1, а вместо этого фактическая абстракция.

Пока мы по теме, возможно, стоит рассмотреть и decorator pattern.

+0

@Scott Дайте мне знать, если это ответит на ваш вопрос –

+0

Отличный ответ, но я не понимаю, почему второй пример - шаблон репозитория. Он просто выглядит как приятный интерфейс с использованием дженериков. –

+0

Вы правы, это не то, что я хотел продемонстрировать, так это то, что мы можем принимать идеи и концепции из существующих шаблонов проектирования и использовать их в другом месте, чтобы они не ограничивались конкретными случаями, когда вы смотрите на «IVehicleOperator 'и' IRepository', все, что на самом деле отличается, это имена, остальная часть кода - это абстракция ... –

9

Учитывая это:

interface ICustomer{ 
    void AddCustomer(Customer toAdd); 
    void UpdateCustomer(Customer toUpdate); 
    Customer GetById(int customerId); 
} 

interface IEmployee 
{ 
    void AddEmployee(Employee toBeAdded); 
    void UpdateEmployee(Employee toUpdate); 
    Employee GetById(int employeeId);  
} 

я бы, вероятно, начать с перепроектирования это следующим образом:

interface IRepository<T> 
{ 
    void Add(T toAdd); 
    void Update(T toUpdate); 
    T GetById(int id); 
} 

Однако, это все еще может быть очень хорошо нарушать Interface Segregation Principle, не говоря уже о том, что так как оно также нарушает шаблон Command-Query Responsibility Segregation (а не архитектуру), он также не может быть ни коварным, ни контравариантным.

Таким образом, мой следующий шаг может быть разделить эти вверх в Role Interfaces:

interface IAdder<T> 
{ 
    void Add(T toAdd); 
} 

interface IUpdater<T> 
{ 
    void Update(T toAdd); 
} 

interface IReader<T> 
{ 
    T GetById(int id); 
} 

Кроме того, вы можете заметить, что IAdder<T> и IUpdater<T> структурно идентичны (они только семантически разные), так почему бы не сделать их один:

interface ICommand<T> 
{ 
    void Execute(T item); 
} 

Чтобы остаться последовательнее, вы можете переименовать IReader<T>, а также:

interface IQuery<T> 
{ 
    T GetById(int id); 
} 

По существу, вы можете уменьшить все до этих двух интерфейсов, но для некоторых людей это может быть слишком абстрактным и нести слишком мало семантической информации.

Однако, я не думаю, что можно дать лучший ответ, потому что помещение ошибочно. Первоначальный вопрос заключается в том, как должен быть разработан интерфейс, но клиент нигде не видно. Как APPP ch. 11 учит нас, «клиенты [...] владеют абстрактными интерфейсами« - другими словами, клиент определяет интерфейс, исходя из того, что ему нужно. Интерфейсы не должны извлекаться из конкретных классов.

Другие учебные материалы по этой теме:

+0

Привет, Марк, верьте или нет, я смотрел ваш курс Pluralsight! Именно это заставило меня искать RAP, в первую очередь, наши интерфейсы «пахло». Я посмотрел на ваш блог, но не мог понять, зачем нужны дополнительные усилия. Мне нужно оправдать доработку команде. – Scott

+3

RAP - это в основном описание желаемого качества базы кода. Отсутствие повторных абстракций часто является * симптомом *, что база кода нарушает [LSP] (http://en.wikipedia.org/wiki/Liskov_substitution_principle). Это не обязательно * плохо. Существуют принципы SOLID для решения определенных проблем с кодом (в основном, ремонтопригодность). Если у вас нет этих проблем, нет никаких оснований пытаться применить «решение». –