Необходимо ли избежать статических классов?

Еще одно преимущество извлечения магического числа в качестве константы дает возможность четко документировать деловую информацию.

public class Foo {
    /** 
     * Max age in year to get child rate for airline tickets
     * 
     * The value of the constant is {@value}
     */
    public static final int MAX_AGE_FOR_CHILD_RATE = 2;

    public void computeRate() {
         if (person.getAge() < MAX_AGE_FOR_CHILD_RATE) {
               applyChildRate();
         }
    }
}
25
задан Alex 22 August 2009 в 03:59
поделиться

7 ответов

Злоупотребление статическими классами можно считать плохой практикой. Но так же может злоупотреблять любой языковой особенностью.

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

Как вы говорите, распространение классов «Помощник» может привести к неприятностям (дизайн, ремонтопригодность, удобочитаемость, обнаруживаемость, другие способности ...). Здесь нет аргументов. Но можете ли вы утверждать, что класс «Помощник» никогда не подходит? Я сомневаюсь в этом.

Действительно, ответственное использование статических классов может иметь большие преимущества для вашего кода:

  • Статический класс Enumerable предоставляет набор методов расширения, которые полюбили большинство из нас. Это логический набор функциональности / бизнес-логики, который не связан ни с каким конкретным типом.
  • Услуги, предоставляемые средой / контекстом: например, ведение журнала, настройка (иногда)
  • Другие (о которых я не могу думать в данный момент:))

Так что нет Вообще это не плохая практика. Просто используйте их с умом ...

23
ответ дан Nader Shirazie 15 October 2019 в 16:42
поделиться

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

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

4
ответ дан Davy8 15 October 2019 в 16:42
поделиться

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

, как думать о oo

синглтоны

статические методы-смерть-тестируемость

2
ответ дан Peter Recore 15 October 2019 в 16:42
поделиться

Существуют потребности, когда у вас есть класс Utility, в котором все методы являются статическими. В этом случае, если вы сделаете класс статичным, это ясно указывает на ваше намерение. И, по крайней мере, в C # нельзя использовать нестатические методы внутри статического класса.

1
ответ дан Tanmoy 15 October 2019 в 16:42
поделиться

Я все время использую статические служебные классы в своем коде для методов, которые вызываются довольно часто, но было бы неудобно для реализации. Примером может служить простой класс журналирования, например:

public static class Logging
{
  public static UpdateAction(int id, object data)
  {
     SqlConnection connection = new SqlConnection("conn string from config file");
     // more logic here...
  }
}

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

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

1
ответ дан Joshua 15 October 2019 в 16:42
поделиться

Нет, это не совсем правильно

Есть множество функций, которые не относятся к экземпляру объекта и не требуют состояния. Например, рассмотрим «математические» функции.

Почти во всех языках есть что-то вроде:

y = Math.round(x);

Таким образом, «круглая» функция статична. Если бы вы были сумасшедшими, вы могли бы спорить с чем-то вроде (C #):

class RoundFunction<T> : GeneralMathFunction<T>
{
    public override T Operate (params object[] variables) {
        ..
    }
}

Но, IMHO, это было бы немного странно.

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

3
ответ дан 28 November 2019 в 21:23
поделиться

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

with cte_ranked as (
select *, row_number() over (partition by UserId order by Time) as rn
from table)
select l.*, datediff(minute, r.Time, l.Time) as gap_length
from cte_ranked l join cte_ranked r on l.UserId = r.UserId and l.rn = r.rn-1

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

Обновление

Мой первоначальный ответ был написан из Mac без базы данных для тестирования. У меня было еще немного времени, чтобы поиграть с этой проблемой и фактически протестировать и измерить, как она работает на таблице 1M записей. Моя тестовая таблица определена следующим образом:

create table access (id int identity(1,1)
    , UserId int not null
    , Time datetime not null);
create clustered index cdx_access on access(UserID, Time);
go

Для выбора записи для любой информации я пока предпочитаю следующий ответ:

with cte_gap as (
    select Id, UserId, a.Time, (a.Time - prev.Time) as gap
    from access a
    cross apply (
        select top(1) Time 
        from access b
        where a.UserId = b.UserId
            and a.Time > b.Time
        order by Time desc) as prev)
, cte_max_gap as (
    select UserId, max(gap) as max_gap
    from cte_gap
    group by UserId)
select g.* 
    from cte_gap g
    join cte_max_gap m on m.UserId = g.UserId and m.max_gap = g.gap
where g.UserId = 42;

Из 1M записи, ~ 47k различных пользователей, результат для этого возвращается в 1 мс в моем тесте маленький экземпляр (теплый кеш), чтение 48 страниц.

Если фильтр UserId = 42 удален, максимальный промежуток и время, когда оно произошло для каждого пользователя (с дубликатами для нескольких максимальных промежутков), потребуют 6379139 чтений, довольно много и занимает 14 секунд на моей тестовой машине.

Время можно сократить вдвое, если требуются только UserId и максимальный разрыв (нет информации , когда максимальный разрыв произошел):

select UserId, max(a.Time-prev.Time) as gap
    from access a
    cross apply (
        select top(1) Time 
        from access b
        where a.UserId = b.UserId
            and a.Time > b.Time
        order by Time desc
    ) as prev
group by UserId

Для этого требуется только 3193448 чтений, только половина по сравнению с предыдущим, и завершился за 6 секунд на 1 млн записей. Разница возникает из-за того, что предыдущей версии необходимо было оценить каждый пробел один раз, чтобы найти максимальный, а затем снова оценить их, чтобы найти те, которые равны максимальному. Обратите внимание, что для этих результатов производительности структура таблицы, которую я предложил с индексом на (UserId, Time), является критической .

Что касается использования CTE и «разделов» (более известных как функции ранжирования) ): это все ANSI SQL-99 и поддерживается большинством поставщиков. Единственной конструкцией, специфичной для SQL Server, было использование функции dateiff , которая теперь удалена. У меня такое чувство, что некоторые читатели понимают термин «агностик» как «SQL с наименьшим общим знаменателем, который понимает и мой любимый поставщик». Также обратите внимание, что использование общих табличных выражений и оператора перекрестного применения используется исключительно для улучшения читаемости запроса. Оба могут быть заменены производной таблицей с помощью простой механической замены. Вот тот же самый запрос , в котором CTE заменены производными таблицами. Я позволю вам судить о его удобочитаемости по сравнению с версией, основанной на CTE:

select g.*
    from (    
        select Id, UserId, a.Time, (a.Time - (
            select top(1) Time 
            from access b
            where a.UserId = b.UserId
                and a.Time > b.Time
            order by Time desc
        )) as gap
        from access a) as g
    join (
        select UserId, max(gap) as max_gap
            from (
                select Id, UserId, a.Time, (a.Time - (
                   select top(1) Time 
                   from access b
                   where a.UserId = b.UserId
                     and a.Time > b.Time
                   order by Time desc
                   )) as gap
            from access a) as cte_gap
        group by UserId) as m on m.UserId = g.UserId and m.max_gap = g.gap
    where g.UserId = 42

Черт, я прыгал, в итоге получится более запутанная лол. Это вполне читаемо, потому что у него было только два CTE для начала. Тем не менее, в запросах с 5-6 производными таблицами форма CTE намного более читабельна.

Для полноты, вот то же преобразование, примененное к моему упрощенному запросу (только максимальные пробелы,

4
ответ дан 28 November 2019 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: