Сделать красиво?
Сделать красиво?
Есть код вида:
static TKnownRanges(){LiOrder.Kreis.NUM_KREIS.Table num_kreis = (LiOrder.Kreis.NUM_KREIS.Table)LiOrder.TLiOrderFactory.Create(LiOrder.TKnownTables.NUM_KREIS);num_kreis.Load();}
Фабрикой создается объект определенного типа.
Фабрикой, потому как там нужна дополнительная инициализация, доступ к элементам которой вне фабрики не желателен.
Понятно, что фабрика возвращает объект базового типа, который надо кастить к нужному типу.
Объектов - много - 3х2х350 штук.
Вопрос - как сделать красиво, т.е. без кастинга?
В сети много инфы.
Если кратко:
- время вызова статического конструктора недетерменировано (зависит от оптимизаций, среды выполнения и использующего кода)
- во время вызова блокируются все другие потоки, кроме одного
- все остальные статические методы и конструкторы класса работают медленнее (пусть на наносекунды, но в быстром цикле на много итераций это может сыграть роль)
- ну и в добавок ограничивается нормальная lazy оптимизация с помощью флага beforefieldinit.
по-моему первый и последний пункт противоречат друг другу. Ленивая инициализация (которая названа преимуществом) это ведь тоже - недетерминированный вызов (который назван недостатком)
Никакого противоречия. Преимущество ленивой инициализации полей - быстродействие. Поля инициализируются по мере необходимости. Инициализаторы полей по сути - простые операции, не связанные между собой. Недостаток недетерминированного вызова конструктора - это метод с логикой, а "фиг знает когда происходящая" логика опасна. Если ты в всё предусмотрел, то твой коллега не знает особенностей и засунет в существующий статический конструктор какой-нибудь код с побочными эффектами и гейзенбагами.
И недетерминированность тут всё же немного разная. Для поля она более-менее предсказуемая. Перед первым использованием конкретного поля оно будет инициализировано. А вот с конструктором уже сложнее - или какое-то поле используется, или объект класса (если класс не статический) создаётся. Намного больше "недетерминированности".
Можно, конечно, возразить, что и в инициализаторы полей легко запихать много логики, а в конструкторе просто
по порядку поля инициализировать. Но это уже философские вопросы.
А если говорить в целом, то и статические конструкторы, и статические инициализаторы - зло. Надо избегать статических состояний всеми силами. Всякие синглтоны и иже с ними. Уж сколько копий поломано в борьбе с багами, которые не дебажатся и не репродуцируются на девелоперских машинах, а народ всё равно туда лезет.
Я бы сказал что для тестов статические методы не страшны до тех пор пока они оперируют только своими параметрами и не создают новых объектов. Т.е. Math.Min() не страшен. А вот какой-нибудь File.CreateTemp(имя файла) - страшен если внутри он лезет к системным свойствам, выйсняет где временный каталог и создает там файл. Вернет нам файл и а него будет тест пихать, например, логи. Подменить такой файловый объект фейком сложно. Тестировать такое уже не так весело - на разных машинах временные каталоги могут быть разные.
Т.е. протестировать сам File.CreateTemp не сложно, а вот тестировать методы, которые его используют - вот где засада.
Если брать примитивные примеры, то проблем, конечно, меньше.
Статические функции нельзя перегрузить и сделать из них заглушки и если для метода Math.Min (int a, int b) это не представляет больших проблем, но если статическая функция затрагивает внешнюю среду, то все становится не так красиво.
У меня на прошлом проекте коллега очень любил статические функции. И это был пипец, там вложенность вызовов была где-то 10 уровней. К тому же добавился анализатор кода, который говорил, что если функция не использует поля класса, то ее надо делать статической. Надо ли говорить, что проект тот был практически нетестируем? :)
Дженерики или вынести метод Load в общий интерфейс и юзать его.
------
Не уверен, что получится с генериками - в объектах не совсем шаблонный код.
Тот же Лоад() - их много.
Первый - без параметров - загружает всю таблицу,
Второй - с параметром - первичный ключ таблицы - загружает единичную строку.
Третий и дале - с параметрами фореин кей - загружают отфильтрованные наборы.
Иногда в третьей группе совпадают количество и типы полей в индексе - надо добавлять поле или делать список-класс...
Можно, конечно, попробовать эллипсис или замену, но мне больше хочется понимать что происходит и иметь ошибку времени компиляции... бо, слишком много кода.
Преимущество ленивой инициализации полей - быстродействие.
-----
Не всегда.
Я как-то получил провал в быстродействии.
Дело было так - на сервер отправлялся запрос и там он считался минут 10-15.
В паралель шла инициализация всего-чего-там-надо-инитить...
Ну а с Лази - инициализация была переложена на после получения ответа... в результате еще минуты полторы все ждало окончания процесса.
Всякие синглтоны и иже с ними.
-----
Да. Только в данном конкретном случае Я не могу перевести табличку в синглетон.
Чтобы это сделать надо существенно поменять ДАЛ-уровень именно для этой таблички.
Иметь же на ДАЛ-уровне ДВА разных класса объектом мне категорически не хочется.
Потому Я делаю синглетоном новый класс, ну а табличка там может быть статической или даже диспаунднутой после загрузки и кэширования...
Как-то так.
Вопрос со статическим конструктором и полем меня на сейчас не беспокоит - его можно поправить когда захочется.
Меня интересует упрощение конструкции с фабрикой.
Я про Фому, а мне про Ерёму. Какая разница, File.Create() или new File().Create()? Что статический, что не статический - если метод с состоянием или с побочными эффектами, то оба плохо тестируемы. А если используется IoC и нет состояния, то оба типа отлично тестируются.
Я хотел сказать, что статические методы в принципе - не зло. Просто их надо правильно готовить. А вот статическое состояние в 99% - зло.
твой код - наиотвратнейшее говно.
-----
А Я разве спорю?
Исходный код - ВБ6-лике - смесь кода управления элементами на форме, синтеза и выполнения запросов и фильтров с обработкой промежуточных результатов.
Кода - много - порядка 2 Гб,
Один проход - т.е. однократное вычисление интересующих пользователя данных в оригинальном варианте занимает 40 минут.
Кое-что Я поправил - нарезал код на классы, оптимизировал некоторые операции - начальная загрузка сокращена с 4-5 минут (время ответа сервера) и последующего отбора (3-4 минуты) до 15-20 секунд. Но все же не все нарезано или разделено правильно - полную задачу-то Я не знаю, доков - нет. Все надо вынимать из кода.
Сейчас борюсь со следующим этапом обсчета - там 99.5% времени занимает выполнение стандартного вызова ДатаТабле.Селект() с детализацией для каждой обрабатываемой детальки. Остальное - обсчет под единицы оборудования с учетом вариантности загрузки оборудования. Вместе с логами и дампами - около 6 часов счета. Если уберу логи и дампы все одно останется полчаса счета. Надо - 3-5 секунд.
И сделать надо всего 2 вещи - избавится от Селекта выполнив счет за один проход... и... сделать код расчета сменяемым вместе со сменой оборудования.
Это - по текущей задаче. Есть еще три, не считая всякой мелочи.
Одна - точно такая же как первая - она собирает исходные данные для обсчета текущей.
Вторая - импортит в систему заказы клиентов (каждый - в своем формате).
Третья - отчеты - там тоже все в стиле ВБ6-лике, но АСПХ-имплементация.
Ковыряю все четыре в параллели... один... естественно - код - гамно галимое и даже выдержать имплементацию в одном стиле - проблемно.
Но - работаю, кое-что меняется к лучшему.
Так вот чтобы сообразить что именно надо делать дальше - надо избавится от лишнего кода.
Один из примеров избыточного кода - неудобно-длинная операция создания таблички.
Идеи как сделать более удобный вариант - всегда обсуждаемо.