2009-07-16 5 views
11

Я просто читаю Гибкие принципы, шаблоны и практики в C# Р. Мартина и М. Мартина, и они предлагают в своей книге сохранить все ваши интерфейсы в отдельном проекте, например. Интерфейсы.Организация интерфейсов

В качестве примера, если у меня есть Gui проект, который содержит все мои пользовательские классы Gui, я буду держать их интерфейсы в Интерфейсы проекта. В частности, у меня был класс CustomButton в Gui, я бы сохранил интерфейс ICustomButton в Интерфейсы.

Преимущество заключается в том, что любой класс, который нуждается в ICustomButton не нужна ссылка на Гуй сам, но только на более легкий вес Интерфейсы проекта.

Кроме того, следует класс в изменении проекта Гуй и, таким образом, потому что это будет восстановлена, только проекты, непосредственно обращаясь к CustomButton необходимо будет перекомпиляция, в то время как те, относящиеся к ICustomButton могут остаться нетронутыми.

Я понимаю, что понятие, но вижу проблемы:

Допустим, у меня есть этот интерфейс:

public interface ICustomButton 
{ 
    void Animate(AnimatorStrategy strategy); 
} 

Как вы можете видеть, это относится к AnimatorStrategy, который является конкретным классом и, следовательно, сидеть в другом проекте, назовет его Анимация. Теперь проект интерфейса должен называться Анимация. С другой стороны, если Анимация использует интерфейс, определенный в Интерфейсах, ему необходимо обратиться к нему.

Циклическая зависимость - «Здесь мы приходим».

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

Я не знаю, хотел бы я иметь дело с этим накладным спросом в разработке.

Любые предложения?

ответ

16

Остерегайтесь всегда, никогда и никогда - особенно почти всех, ни одного, ни каждого.

Если вы всегда ставить все из вас интерфейсов в отдельной сборке? Нет - не обязательно.

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

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

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

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

+0

Спасибо за ваш ответ. Я пытался разглядеть вокруг DIP (принцип инверсии зависимостей). Хотя теоретически это выглядит хорошо (классы нижнего уровня в зависимости от классов более высокого уровня, а не наоборот), на практике его трудно реализовать и на практике. Намного проще просто поддерживать интерфейсы вместе с классами, которые их реализуют. Думаю, в этом случае я согласен с вашим предложением только «усложнять» дизайн таким образом, если это станет необходимым. –

3

Простой ответ: AnimatorStrategy также должен быть интерфейсом. Реализация для AnimatorStrategy находится в проекте Animation, но интерфейс будет реализован в проекте AnimationInterfaces или Interfaces (по мере необходимости).

У меня не обязательно был бы один проект интерфейса, но один для каждой функциональной группировки. Клиенты Animation будут ссылаться на AnimationInterfaces, а клиенты Gui будут ссылаться на GuiInterfaces.

Таким образом, вы сохраняете свои контракты, не смешиваясь ни с какой реализацией.

Для чего я предпочитаю идти в обратном порядке с соглашениями об именах. Поместите интерфейсы в анимацию и реализации в AnimationImpl (или аналогичные).Таким образом вы ссылаетесь на функциональное имя.

-2

Как определить интерфейс с методами, использующими общие типы? Например.

public interface IThing 
{ 
    int DoMyThing<T>(T variable); 
} 

Если вам нужно какое-то ограничение на T variable, вы могли бы сделать:

int DoMyThing<T>(T variable) where T : IOtherInterface; 

Где IOtherInterface также определяется в проекте Interfaces. Затем, пока ваш конкретный класс наследует и от IThing, и от IOtherInterface, вы можете использовать метод DoMyThing и передать экземпляр вашего конкретного класса без циклической зависимости. Ваш код может стать:

public interface ICustomButton 
{ 
    void Animate<T>(T strategy) where T : IAnimateable; 
} 

интерфейс не имеет никакого отношения к конкретному классу AnimatorStrategy.

+1

Как это уместно? O_o –

+1

Это хорошая идея, но мне больше нравится взломать. Интерфейсы заключаются в контрактах. Как Т объясняет, какую стратегию можно ожидать? Если бы у меня был IAnimateable, мне бы не понадобился T. –

1

Я думаю, что циклические зависимости могут быть неизбежны, даже если вы объявляете все как интерфейсы, потому что в сборке Foo может быть метод, который принимает параметр IBar и метод в панели сборки, который принимает параметр IFoo.

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

Смежные вопросы