Вы бы абстрагировали свои запросы LINQ в методы расширения? - PullRequest
5 голосов
/ 08 июня 2011

В моем текущем проекте мы поставили перед собой некоторые цели для метрик кода «Индекс ремонтопригодности» и «Циклометическая сложность». Индекс ремонтопригодности должен быть 60 или выше, а циклометическая сложность 25 или меньше. Мы знаем, что индекс ремонтопригодности 60 и выше довольно высокий.

Мы также используем много linq для фильтрации / группировки / выбора объектов. Я обнаружил, что эти запросы linq не так высоко оценивают индекс ремонтопригодности. Абстрагирование этих запросов в методы расширения дает мне более высокий индекс сопровождения, и это хорошо. Но в большинстве случаев методы расширения больше не являются общими, потому что я использую их с моими типами вместо общих типов.

Например, следующий метод linq-запроса против метода расширения:

Запрос Linq

List.Where(m => m.BeginTime >= selectionFrom && m.EndTime <= selectionTo)

Метод расширения:

public static IEnumerable<MyType> FilterBy(this IEnumerable<MyType> source, DateTime selectionFrom, DateTime selectionTo)
{
    return (IEnumerable<MyType>)source.Where(m => m.BeginTime >= selectionFrom && m.EndTime <= selectionTo);
}

List.FilterBy(selectionFrom, selectionTo);

Метод расширения дает мне улучшение индекса ремонтопригодности на 6 пунктов и дает хороший беглый синтаксис. С другой стороны, мне нужно добавить статический класс, он не является универсальным.

Любые идеи о том, какой подход будет иметь вашу пользу? Или, может быть, есть разные идеи о том, как реорганизовать запросы linq для улучшения индекса сопровождения?

Ответы [ 5 ]

5 голосов
/ 08 июня 2011

Вы не должны добавлять классы ради метрики. Любые метрики призваны сделать ваш код лучше , но, следуя слепым правилам, даже лучшим правилам, может фактически повредить вашему коду .

Не думаю, что стоит придерживаться определенных индексов сопровождения и сложности. Я считаю, они полезны для оценки старого кода , т. Е. Когда вы унаследовали проект и вам необходимо оценить его сложность. Однако абсурдно извлекать метод , потому что вы не набрали достаточно очков.

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

Это единственная метрика, которой вы должны следовать, но это не всегда метрика, с которой все согласны ...

Что касается вашего примера, если один и тот же запрос LINQ используется снова и снова, имеет смысл создать папку EnumerableExtensions в Extensions и извлечь ее там. Однако, если он используется один или два раза или может быть изменен, подробный запрос будет намного лучше.

Я также не понимаю, почему вы говорите , что они не являются общими с несколько отрицательными коннотациями. Вам не нужны дженерики везде! На самом деле, при написании методов расширения вы должны учитывать наиболее конкретные типы, которые вы можете выбрать, чтобы не загрязнять набор методов других классов. Если вы хотите, чтобы ваш помощник работал только с IEnumerable<MyType>, объявлять метод расширения точно для this IEnumerable<MyType> абсолютно не стыдно. Кстати, в вашем примере есть избыточное приведение . Избавься от этого.

И не забывайте, инструменты глупы. Мы тоже, люди.

4 голосов
/ 08 июня 2011

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

Что, по вашему мнению, подходит для вашего приложения?

2 голосов
/ 08 июня 2011

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

Для меня это касается не только метрик, но и повторного использования кода.См. Следующие псевдо-примеры:

var x = _repository.Customers().WhichAreGoldCustomers();

var y = _repository.Customers().WhichAreBehindInPayments();

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

Кроме того, они являются составными:

var z = _repository.Customers().WhichAreGoldCustomers().WhichAreBehindInPayments();

ИМХО этовыигрышный подход.

Единственная проблема, с которой мы столкнулись, заключается в том, что существует ошибка ReSharper, из-за которой Intellisense для методов расширения сходит с ума.Вы вводите «.Whic», и он позволяет вам выбрать метод расширения, который вы хотите, но когда вы «вкладываете» его, он добавляет в код что-то совершенно другое, а не метод расширения, который вы выбрали.Я подумал о переходе с ReSharper для этого, но ... нет:)

1 голос
/ 08 июня 2011

НЕТ: в этом случае я бы проигнорировал цикломатическую сложность - то, что у вас изначально было лучше,

Спросите себя, что является более объяснительным.Это:

List.Where(m => m.BeginTime >= selectionFrom && m.EndTime <= selectionTo)

или это:

List.FilterBy(selectionFrom, selectionTo);

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

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

0 голосов
/ 08 июня 2011

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

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

Если в вашей системе несколько объектов, для которых распространена концепция наличия даты, рассмотрите интерфейс, возможно, IHaveADate (или что-то лучше: -)

public static IQueryable<T> WithinDateRange(this IQueryable<T> source, DateTime from, DateTime to) where T:IHaveADate

(IQueryable интересен. Я не думаю, что IEnumerable может привести к нему что-то позорное. Если вы работаете с запросами к базе данных, то это может позволить вашей логике появляться в окончательном SQL, который отправляется на сервер, который это хорошо. Есть потенциальная ошибка со всем LINQ, что ваш код не выполняется, когда вы ожидаете, что он будет)

Если диапазоны дат являются важной концепцией в вашем приложении, и вам необходимо быть последовательным в отношении того, начинается ли диапазон в полночь в конце «EndDate» или в полночь в его начале, тогда класс DateRange может быть полезен. Тогда

public static IQueryable<T> WithinDateRange(this IQueryable<T> source, DateRange range) where T:IHaveADate

Вы также можете, если хотите, предоставить

public static IEnumerable<T> WithinDateRange(this IEnumerable<T> source, DateRange range, Func<DateTime,T> getDate)

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

var filtered = myThingCollection.WithinDateRange(myDateRange, x => x.Date)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...