Wednesday, July 22, 2020

Enums handling: NotSupported vs ArgumentOutOfRange exception?


-----------------------------------------------------------------

Hey guys!

Lets consider the following C# code (the issue is applicable for many languages as well):

public enum Frequency
       { 
            Monthly = 1
        }
 
public int Method(Frequency value)
        {
            switch (value)
            {
                case Frequency.Monthly:
                    return 1 + 2 + 3; // Some logic
                default:
                    throw new Exception($"{value} can not be handled!");
            }
        }

Programming language does not matter here, because we are going to argue about a valid exception in default clause. What are your variants? Below are standard handling for similar situation and I believe we should always follow it:

  • ArgumentOutOfRangeException should be thrown if you pass invalid enum value, like following: Method((Frequency) -1) - this is allowed by C#, but can be considered like a hack.
  • NotSupportedException should be thrown if you pass a valid enum value (e.t. enum has also additional value Frequency.Daily and we pass it into this method) into a method which does not know how to process it. There is no handling for this value right now, so we must inform calling code that the method does not support/provide an implementation for the passed value.

///////////////////////////////////////////////////////////////////////////////

What we have in our code above:

  • Enum has the only value which is handled by switch operator. So right now ArgumentOutOfRangeException is not bad.
The situation is different if enum has more values (e.t. Frequency.Daily). NotSupportedException must be thrown for Frequency.Daily as it is not supported by the method (additional case-clause(s) should be added). ArgumentOutOfRangeException in default clause must be left w/o change for handling invalid values.

///////////////////////////////////////////////////////////////////////////////

And do you know what? Sometimes we face with such code (keeping in mind that enum still has the only value):

public int Method(Frequencyvalue)
        {
            switch (value)
            {
                case Frequency.Monthly:
                    return 1 + 2 + 3; // Some logic
                default:
                    throw new NotSupportedException($"{value} is not supported!");
            }
        }
 
NotSupportedException is thrown in default clause only for invalid values, because we have the only value in our enum which is handled by switch operator.
Invalid values are out of range and this is why from my point of view NotSupportedException does not explain a real reason of this exceptional case.

But why we have NotSupportedException here? 

Some developers who vote for this approach explain it in a simple way: "Right now this method supports all existing values and if our enum will be updated with new value in the future, the method will throw a valid exception w/o method updating."

Hmmm... Quite rational point from the first sight, similar on Open/Close principle, but is it true? Lets take a deep look: there is a time period when this code has wrong semantic with NotSupportedException for out of range values (until a new enum value is introduced). Are you OK with this?

Also they say "We avoid redundant method's change in the future". Do you believe them? Which exception do we expect in this case Method((Frequency) -1)  (if we allow such casting)? I would expect ArgumentOutOfRangeException. But NotSupportedException will be thrown even in this case! To distinguish these cases we must update our method or it will have a wrong semantic AGAIN...


Also lets think from another side: how often do we update an enum and leave such switch-methods w/o update? I would say this is an exceptional case in normal life: usually with introducing a new value for enum, we introduce a new branch of logic. As a result you should review all code places where the updated enum is used and update them as well.

Conslusion

You should not think "too much" about future of the code, just keep in mind that your code must be clear  and clean here and now!

Think out of the box! Make your right switch! :)

P.S. Did you hear about Enum.IsDefined method? It can be very useful to determine when ArgumentOutOfRangeException can be thrown ;)

Monday, January 27, 2020

"Чистая архитектура" Роберта Мартина. Кратчайший конспект книги

Архитектура = дизайн.

Архитектура включает в себя функциональную и структурную составляющие.

Знания о хорошей архитектуре в основном - правила как не надо делать.

Парадигмы программирования

Структурное программирование Дейкстры - отказ от оператора GOTO или прямой передачи управления в коде.

ООП появилось с возможностью помещать часть стека вызова функции (вместе с локальными переменными) и указателей функций в "кучу" памяти. Так родились конструкторы и методы классов.

Инкапсуляция была возможно ещё и в не ОО-языках. Но в книге инкапсуляция отождествяется с сокрытием данных, с этим категорически не согласен. Это то же самое, что говорить SSL = TLS. Как по мне, инкапсуляция - отсутствие возможности обращаться к данным и функциям "объекта" напрямую из памяти, минуя ссылку на сам объект. При этом все данные могут быть доступны всем, у кого есть ссылка на объект.

Наследование тоже не новинка ООП.

Полиморфизм - вот что реально важно. С помощью его стала возможной инверсия зависимостей, т.к. вызывающий код перестал зависеть от реализации вызываемого кода. А это и независимость развёртывания компонент, разработки и другие плюшки.

SOLID принципы

Single Responsibility Principle - у модуля/класса/.. должна быть одна и только одна причина для изменения => модуль должен отвечать за одного и только одного пользователя => модуль должен отвечать только за одного и только одного актора.

Признаки нарушения принципа: 1) непреднамеренное дублирование; 2) слияния (мержи).

Open-closed principle 
Достигается делением системы на компоненты и упорядочением их зависимостей в иерархию, защищающую компоненты уровнем выше от изменений в компонентах уровнем ниже.

Принцип подстановки Барбары Лисков
Если для каждого объекта типа S существует такой объект типа T, что для всех программ P, определённых в терминах T, поведение P не изменяется при подстановке объекта S вместо T, то S является подтипом T.

Принцип можно использовать при создании иерархии наследования.

Проблема "квадрат/прямоугольник"

Interface Segregation Principle 
Актуален для статических языков программирования, т.е является проблемой языка, а не архитектуры.
Опасно создавать зависимости от модулей, содержащих больше, чем требуется.

Dependency Inversion Principle
Интерфейсы стабильнее конкретных реализаций, менее изменчивы. Наследование - самый жесткий тип отношения в исходном коде. Почему "принцип инверсии зависимости"? - "Архитектурная" линия пересекает диаграммы компонент/классов так, что "поток управления" обращён относительно зависимостей исходного кода.

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

////////////////////////

Связность компонентов

Reuse/release Equivalence Principle
Common Closure Principle - аналог SRP для компонентов.
Common Reuse Principle - аналог ISP для компонентов - классы, не имеющие тесной связи, не должны включаться в один компонент. Не создавайте зависимостей от чего-либо неиспользуемого.

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

Сочетаемость компонентов

Принцип ацикличности зависимостей (еженедельные сборки и устранение циклических зависимостей). "Зацикленный" набор компонент превращается в один большой компонент.

Как разорвать цикл: 1) инверсия зависимостей; 2) создание нового компонента.

Принцип устойчивых зависимостей - зависимости должны быть направлены в сторону устойчивости. Метрика стабильности ( Fan-out/(Fan-in + Fan-out) ) должна уменьшаться в направлении зависимости.

Принцип устойчивости абстракций - устойчивость компонента пропорциональна его абстрактности.


Что такое архитектура

Предназначение архитектуры - поддержка жизненного цикла системы. Как можно дольше иметь как можно больше вариантов.

Разработка, развёртывание, эффективность работы, сопровождение, сохранение разнообразия вариантов.

Закон Конвея:
Любая организация, разрабатывающая систему, невольно будет формировать дизайн, структура которого повторяет структуру взаимодействий внутри этой организации.

Скромный объект (Humble object)

Разделить поведение на два модуля, один из которых содержит всё, что с трудом поддаётся тестированию, код в этом объекте упрощается до предела - это скромный объект.

Шлюзы к базам данных
Преобразователи данных (ORM)

Неполные границы

Полноценные архитектурные границы обходятся очень дорого (двусторонние пограничные интерфейсы, структуры для входных и выходных данных, компоненты, компилируемые и развертываемые независимо).
Способы реализации неполных границ:
- сделать всё как в полной границе, но оставить всё в одном компоненте
- одномерные границы
- фасады
Одна из задач архитектора - решить, где провести архитектурную границу и как её реализовать, частично или полностью.

База данных - это деталь

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

Tuesday, May 8, 2018

"Visual studio verifying your model is synchronized with your source files..."


Problem:
“Visual studio verifying your model is synchronized with your source files. Your database projects will be ready after 100500 operations are completed.” message during solution loading, which takes a huge time to complete.


Explanation:
At first, we see that some model and source files are synchronized. Which model and which source files?

It’s obvious that issue related to DB-projects (we have several DB projects in our solution). So, => source files are files from DB-projects.
When we open any DB-project root folder, we can find <ProjectName>.dbmdl file. Actually, it is a file with database model generated by VS. Sync between this model and sources is mentioned in the “concern” message.

Having this model, we remove DB-project dependency on a design DB (real DB on your DEV-machine) which is used for VS intellisense, validation and other useful VS-features. Also it is used as a cache to improve performance when working with DB-project.
I just can suggest, that this is done for:
1) to have more layers J;
2) to have environment-agnostic DB-projects, I mean that DB-project can be developed with a wide range of DB servers (SQL CE, localdb, SQL Server and so on) installed in your DEV machine.





Sure, it makes sense!


But have we a stub DBs on our DEV boxes? Sure!
In my case I have figured out that empty databases exist on (localdb)\ProjectsV13 server (I caught its name when VS was loading the solution) which corresponds to our DB-projects.


My suggestion was that DB’s tables/sp/functions will be restored on this server, but it seems that they are handled in-memory from .dbmdl file.

So, on every loading we have interactions Sources <-> Model and Model <-> DB which decrease our performance.

Solutions:

  1. Add solution folder to antivirus exceptions;
  2. Add folder where design DBs are located to antivirus exceptions as well (in my case “C:\Users\AndreyMusky”);
  3. Uncheck “Auto Create Statistics” and “Auto Update Statistics” options on each DB;
  4. Check “Boost SQL Server Priority” in localdb -> Properties -> Processor section;
  5. Suggest your idea for improvement based on your experience;
  6. Believe that all we do help us;

References:

The most useful resource I’ve found is the following. At least it does not break the theory above!

Friday, March 2, 2018

Цитаты

однажды перевернувшие мой мир.

Николай Амосов:
Адаптация - враг счастья. Ищите себе страсть в работе - будете счастливы. Не все время, но будете...

Сергей Королёв:
Можно сделать быстро, но плохо, а можно — медленно, но хорошо. Через некоторое время все забудут, что было быстро, но будут помнить, что было плохо. И наоборот.

Критикуешь чужое, предлагай своё. Предлагая — делай.

Порядок освобождает мысль.

Санди Мец
Уже написанный код имеет мощный авторитет. Само его существование свидетельствует о его правильность и нужности.

Аноним или просто не помню автора:
Hard things are hard.

Книги которые читают сейчас, это настоящее, а которые перечитывают - будущее.

Поэтому можешь ты не быть, а музыкантом быть не можешь

Разгребая кучи задач

Ответы к задачам из статьи Куча задач:


  1. 42, 235000, 6, 6, 25?, 432, 1, 81, 377

Wednesday, February 28, 2018

Куча задач


Здесь я буду перечислять ссылки на наборы задач, которые можно порешать в свободное время. А лучше - в рабочее! :)

Ответы на задачи можно найти в статье Разгребая кучи задач.
  1. https://nplus1.ru/material/2018/01/31/tinkoff-analytics?utm_source=oldlentach&utm_medium=social&utm_term=hrtest&utm_campaign=lentach--tut-podehala-interesnaya-vakansi

*Результат появится ПОД самой последней задачей, когда вы введёте ВСЕ ответы, нажав на кнопку OK справа от поля ввода.

      2. Поступите ли вы в пятый класс одной из лучших школ страны?

Thursday, February 8, 2018

Кот-ревью или soft-версия сурового код-ревью

      Для того, чтобы код ревью в команде проходил эффективно и способствовал хорошему микроклимату в команде, я выделил для себя следующие принципы:

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

   - проводить дизайн-ревью: мега полезная штука, ведь так мы растём гораздо быстрее и гораздо меньше тратим времени на переделки / доработки кода в дальнейшем;
   
   - тривиальные правки не заслуживают того, чтобы о них упоминать. Смиритесь с тем, что мир неидеален, а ваше время дороже дешёвых споров о названии переменной;

   - если вы оставляете замечания/комментарии к коду, который не менялся, помечайте их как опциональный (к примеру: out of scope, optional);

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


      Это принципы, которые я выделил специально для себя, которые я постоянно нарушал и которые приводили к конфликтам. А нашёл я их здесь. Статья не моя, но очень полезная и интересная. Надеюсь, дойдут руки почитать литературу по peer review, упомянутую в этой статье :)

     Ведь тогда работа в любой команде станет просто милым ревью котиков и того, что они натворили...