Использование методов расширения - Разве это не плохой дизайн? - PullRequest
4 голосов
/ 12 ноября 2008

Я только начал изучать .NET 3.5, поэтому, пожалуйста, прости меня, если этот тип вопросов задавался ранее. Я борюсь с достойным использованием методов расширения, потому что я только что загрузил suteki shop, предложение электронной коммерции MVC. В этом проекте есть довольно стандартный шаблон репозитория, который расширяет IRepository.

Для расширения основных функций, предоставляемых этим интерфейсом, используются методы расширения, т. Е .:

.
public static class CategoryRepositoryExtensions
{
    public static Category GetRootCategory(this IRepository<Category> categoryRepository)
    {
     return categoryRepository.GetById(1);
    }
}

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

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

Кажется, что более старый способ создания IRepository, а затем расширения, который позволяет гораздо лучше видеть то, что требуется, например,

ICategoryRepoitory : IRepository<Category>
{
     Category GetRootCategory();
}

Так что я предполагаю, что мой вопрос заключается в том, что использование методов Extention кажется кому-то неправильным? Если нет, то почему? Разве я не должен стонать об этом?

EDIT:

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

Полагаю, моя проблема в том, застряли ли конкретные реализации доступа к данным в методе расширения в сборке механизмов доступа к данным.

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

Ответы [ 4 ]

5 голосов
/ 13 ноября 2008

Дело в том, что если вы правильно реализовали все IRepository<T>, вам (как разработчику уровня данных) вообще не нужно знать о корневых категориях. Для области применения этого метода расширения предполагается, что любой репозиторий категорий будет иметь корневую категорию ID 1. Это вполне может быть вполне разумным и полезным: никто не должен создать производный класс (который может быть непрактичным - слой данных может иметь фабрики и т. д., что затрудняет извлечение из реализаций).

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

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

4 голосов
/ 09 октября 2009

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

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

1 голос
/ 13 ноября 2008

Это не одно и то же. Метод расширения - это синтаксическое удобство, не более того, и, конечно, это не договорное требование. Вы не «внедряете» их в свои классы. Для вашего примера выше:

using CategoryRepositoryExtensions;
...
Category c = r.GetRootCategory();

равно точно так же, как:

Category c = CategoryRepositoryExtensions.GetRootCategory(r);

и, следовательно, нет дополнительных требований к исполнителю интерфейса.

0 голосов
/ 13 ноября 2008

Да, в приведенном вами примере это кажется нелогичным, и причина в том, что вы работаете с одним объектом на одном уровне. Я считаю, что методы расширения наиболее полезны, когда вы работаете с коллекциями IQueryable / IEnumerable.

Например, Давайте рассмотрим 2 сценария:

  • Сценарий 1: получить список всех заказов, которые содержат продукт x

  • Сценарий 2: получить список всех заказов, которые содержат продукт x и были отправлены в почтовый индекс y

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

Сценарий 1

<IEnumerable> orders1Enumerable = OrderRepository.GetAllOrders()
                                     .ContainingProductCode(x);

Сценарий 2

<IEnumerable> orders2Enumerable = OrderRepository.GetAllOrders()
                                     .ContainingProductCode(x)
                                     .WithShippingZipCode(y);

Когда вы делаете это, фреймворк создает соответствующий SQL на лету, связывая эти методы расширения

Надеюсь, это поможет

...