Информация об изменениях

Сообщение Re[16]: Интерфейсы и реализация от 24.10.2020 13:34

Изменено 24.10.2020 13:42 Mystic Artifact

Re[16]: Интерфейсы и реализация
Здравствуйте, Sharov, Вы писали:

S>Подождите, мы DI рассматриваем в контексте IoC контейнеров, где мы в явном виде new никогда не вызываем, а используем,

S>например, абстрактную фабрику. Ну так вот IoC у меня созвучен с dip, в том плане, что это dip для контроля управления.
S>А dip говорит, что завязываться на детали (реализации) плохо, лучше завязываться на абстракции. Т.е. мы куда-то встраиваемся
S>(во фреймворк) через интерфейсы и абстрактные классы.

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

Раз, для тебя близок ASP.NET Core, — то далеко ходить и не будем. Можно взять его ILogger, как пример. К нему, как к инфраструктурному компоненту должны предъявляться повышенные требования, а на деле — он сам себя закрывает за ущербным интерфейсом из 3 методов, при чём поведение третьего метода — не гарантированно (scoped logging). Реально используемые методы (типа LogDebug) — выполнены вообще в виде экстеншнов, хотя здесь практически нет места свободному расширению, как в LINQ, которые в итоге форсируют использовать using namespace, даже если у тебя доступ к логгеру уже есть.

Ну и вишенка на торте, что практически у любого логгера первое, что он делает, это условие на подобии такого:

  if (logEventLevel < _currentMinimumLevel) return;
  // slow path


Так вот, в здоровых фреймворках, это условие всегда инлайнится, позволяя опустить сам вызов полностью. В добавок даже если вызов и произойдет — в вызове на подобии LogDebug, даже нет смысла в коде передавать уровень как параметр — уменьшая этим самым непосредственно размер исполнимого кода, при этом сохранив цепочку вызовов минимальной (у MS.Logging этого не происходит, спасибо экстеншн методам).

Ровно как и здоровые фреймворки, никогда не будут делать так:
public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args)


Предпочитая следующие вызовы:
public void LogTrace<T1>(Exception exception, string message, T1 arg1);
public void LogTrace<T1, T2>(Exception exception, string message, T1 arg1, T2 arg2);
public void LogTrace<T1, T2, T3>(Exception exception, string message, T1 arg1, T2 arg2, T3 arg3);


Да, да, всё ради того же: избавиться от холостых выделений памяти, да и generic инвокация окажется короче. Но, опять же, это невозможно, т.к. мы ограничены ущербным интерфейсом.

Это не особо важно для уровней Info и выше, т.к. они практически всегда логгируются, и механика материализации съест немало, но уровни Trace и Debug — в нормальном режиме практически всегда подавлены и достаточно чувствительны к этому. (И не надо петь про дешевость вызовов — любой, даже не виртуальный вызов съедает слот в предсказателе переходов.)

Ну и с практической точки зрения, для пользователя, абсолютно фиолетово, что использовать ILogger, Logger, тем более в контейнере можно ещё зарегистрировать и NLog.Logger и Serilog и всё это будет вместе жить через адаптеры, при чём бэкэндом будет любой из них. В конце концов Microsoft.Extensions.Logging — по отношению к твоему коду — просто ещё одна бибилиотека со своим интерфейсом, и фактически — со своим поведением и с единственной реализацией.

Там есть и другие нюансы у них, но по сути, я вел не к тому, какое плохое или хорошее у них логгирование (оно вполне достаточное и фреймворк достаточно универсальный... что бы его не использовать), а просто пример, того, как легко запереть себя в "абстракциях" отрезав любые эффективные решения как класс. В данном случае — абстракция вообще не нужна.
Re[16]: Интерфейсы и реализация
Здравствуйте, Sharov, Вы писали:

S>Подождите, мы DI рассматриваем в контексте IoC контейнеров, где мы в явном виде new никогда не вызываем, а используем,

S>например, абстрактную фабрику. Ну так вот IoC у меня созвучен с dip, в том плане, что это dip для контроля управления.
S>А dip говорит, что завязываться на детали (реализации) плохо, лучше завязываться на абстракции. Т.е. мы куда-то встраиваемся
S>(во фреймворк) через интерфейсы и абстрактные классы.

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

Раз, для тебя близок ASP.NET Core, — то далеко ходить и не будем. Можно взять его ILogger, как пример. К нему, как к инфраструктурному компоненту должны предъявляться повышенные требования, а на деле — он сам себя закрывает за ущербным интерфейсом из 3 методов, при чём поведение третьего метода — не гарантированно (scoped logging). Реально используемые методы (типа LogDebug) — выполнены вообще в виде экстеншнов, хотя здесь практически нет места свободному расширению, как в LINQ, которые в итоге форсируют использовать using namespace, даже если у тебя доступ к логгеру уже есть.

Ну и вишенка на торте, что практически у любого логгера первое, что он делает, это условие на подобии такого:

  if (logEventLevel < _currentMinimumLevel) return;
  // slow path


Так вот, в здоровых фреймворках, это условие всегда инлайнится, позволяя опустить сам вызов полностью. В добавок даже если вызов и произойдет — в вызове на подобии LogDebug, даже нет смысла в коде передавать уровень как параметр — уменьшая этим самым непосредственно размер исполнимого кода, при этом сохранив цепочку вызовов минимальной (у MS.Logging этого не происходит, спасибо экстеншн методам).

Ровно как и здоровые фреймворки, никогда не будут делать так:
public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args)


Предпочитая следующие вызовы:
public void LogTrace<T1>(Exception exception, string message, T1 arg1);
public void LogTrace<T1, T2>(Exception exception, string message, T1 arg1, T2 arg2);
public void LogTrace<T1, T2, T3>(Exception exception, string message, T1 arg1, T2 arg2, T3 arg3);


Да, да, всё ради того же: избавиться от холостых выделений памяти, да и generic инвокация окажется короче. Но, опять же, это невозможно, т.к. мы ограничены ущербным интерфейсом.

Это не особо важно для уровней Info и выше, т.к. они практически всегда логгируются, и механика материализации съест немало, но уровни Trace и Debug — в нормальном режиме практически всегда подавлены и достаточно чувствительны к этому. (И не надо петь про дешевость вызовов — любой, даже безусловный переход (jmp) съедает слот в предсказателе переходов.)

Ну и с практической точки зрения, для пользователя, абсолютно фиолетово, что использовать ILogger, Logger, тем более в контейнере можно ещё зарегистрировать и NLog.Logger и Serilog и всё это будет вместе жить через адаптеры, при чём бэкэндом будет любой из них. В конце концов Microsoft.Extensions.Logging — по отношению к твоему коду — просто ещё одна бибилиотека со своим интерфейсом, и фактически — со своим поведением и с единственной реализацией.

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