Чистый код, или  "Встаньте! Мне нужно убраться".

Чистый код, или "Встаньте! Мне нужно убраться".

Больница № 389, после длительных и безуспешных попыток найти уборщицу, преобразована в грязелечебницу.

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

Для начала хотелось бы ввести два понятия: «писать» и «педалить». Писать у нас будет означать так как надо делать, а педалить, в свою очередь, как не надо. И так, включаем пылесос, начнем-с. Начнем от печки, а точнее от дома. Классный дом — это когда все сделано как надо, а главное фундамент заложен отлично, что гарантирует долговечность. Проведя аналогию с программированием, мы строим каждый день свои дома, и вот как в идеале выглядит чертеж.

Архитектура приложения

И, как тут показано, основой дома является чистый код. Кто-то возразит, что основой является архитектура, то есть то, что задумал архитектор, но я смотрю со стороны обычно строителя Степана, который все выполняет, что задумал архитектор. Так же помимо чистого кода есть еще ряд немаловажных блоков, многие из них будут описаны в следующих статьях. Как хороший строитель, Степан не должен использовать материалы какие попало, к примеру, вместо цемента при кладке кирпича использовать монтажную пену, похоже, а результат будет вообще не тот. Так и в программировании, не стоить педалить js функции в c# коде.

string script = @"<script type="text/javascript">
//
//как правило
//много
//очень много
//строк javascript
//
</script>";
this.Headers.Controls.Add(new Literalcontrol("\r\n"+script));

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

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

Документирование… вопрос вообще неоднозначный, все к нему относятся равнодушно, ведь писать комментарии, когда имя метода итак все объясняет, нелепо. Но если в методе реализована сложная логика, которая не является известным стандартным алгоритмом, то стоит описать ее, мало кто будет потом разбираться в вашем коде, помогите ему сразу, а то и вообще время пройдет и сами забудете, что писали, к примеру, заставили править и будет трата времени.

Нейминг – не используйте в именах переменных одну букву или сокращения (одну букву – разве что в итераторе). Нейминг классов – не педалим имена, которые ничего не отображают, которые имеют окончания на *Processor, *Info, не используйте аббревиатуры. Именем должно быть существительное и со смыслом. Нейминг методов, тут многие уже все итак прекрасно знают, описать имя метода, так чтобы отображалась суть, которую он выполняет.

Булевые выражения – все мы что то проверяем сравниваем, но прежде всего, ни вкоем случае не педалим такое:

if(isLoggedIn==true)
{ 
   //какой-то код 
}

loggedIn и так содержит только true или false значения, и сравнивать их никчемую Не стоит также и педалить подобного рода чепуху:

bool goingToBar;

if(cashInWallet > 10)
{
   goingToBar = true;
}
else{
   goingToBar = false;
}

Не отрицайте отрицание, да почти схоже с тавтологией, но нагляднее :

if(!isNotLoggedIn)
{
   //какой-то код
}

Цель чистого кода - это сделать как можно компактнее код приложения, и так написать, чтоб человек смог его читать как роман, попивая чек иль чего покрепче (в нерабочее время есессно!) поэтому педалинг такого выражения не будет придавать элегантности моменту чтения или распития и чтения .

int pointsCount;
if(isSpeaker)
{
   pointsCount = 0;
}
else
{
   pointsCount = 50;
}

А правильный вариант выглядит вот так:

int pointsCount = isSpeaker ? 0:50;

Не надо сравнивать со string какие-либо значения.

if(employeeType == “manager”)
{
   //какой-то код
}

А правильный вариант:

if(employee.Type == EmployeeType.Manager)
{
   //какой-то код
}

Магические числа, вообще прекрасный момент, сравниваем переменную с 25, а что такое 25 ? ответ : 25. И это довольно частая ошибка, ее продолжают и продолжают педалить.

if(age >= 21)
{
   //какой-то код
}

А правильный вариант:

const int legalDrinkingAge = 21;
if(age >= legalDrinkingAge)
{
   //какой-то код
}

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

if(employee.Age > 55 && employee.YearsEmployed > 10 && employee.IsRetired == true)
{
   //какой-то код
}

А правильный вариант:

bool eligibleForPension = employee.Age > MinRetirementAge && employee.YearsEmployed > MinPensionEmploymentYears && employee.IsRetired;
/*переменная имеет осмысленное название, которое дает понять сразу, 
что проверяется право выхода на пенсию работника*/
if(eligibleForPension)
{
   //какой-то код
}

Будьте логичны, изучайте свои инструменты, которыми работаете. Степан не стал бы пахать поле лошадью, если у него есть комбайн, так не стоит и нам. Как пример, использование LINQ для фильтрации данных:

var matchingUsers = new List<User>();

foreach(var user in users)
{
    if(user.AccountBalance < minimumAccountBalance &&
       user.Status == Status.Active)
       {
           matchingUsers.Add(user);
       }
}

return matchingUsers;

А лаконичнее будет: 

return users.Where(u => u.AccountBalance < minimumAccountBalance)
            .Where(u => u.Status == Status.Active);

Количество проверок не должно превышать вложенность в три уровня

if(some){
    if(anotherSome){
        if(anotherSecondSome){
            //какой то код
        }
    }
}

Методы

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

  • Если код повторяется – выделяй его в метод
  • Если в коде может произойти ошибка, сделай так чтоб она проявлялась максимально быстро, а не в самом конце выполнения метода
  • Возвращай значение как можно быстрее. Куй железо пока горячо, тык сказать.

И если с первым все ясно и не надо обьяснять, то по оставшимся двум приведу пример.Возвращай как можно раньше :

private bool ValidateUsername(string username)
{
    var isValid = false;
    const int MinUsernameLength = 6;
    if(username.Length >= MinUsernameLength)
    {
        const int MaxUsernameLength = 25;
        if(username.Length <= MaxUsernameLength)
        {
            var isAlphaNumeric = username.All(Char.IsLetterOrDigit);
            if(isAlphaNumeric)
            {
                if(!ContainsCurseWords(username))
                {
                    isValid = IsUniqueUsername(username);
                }
            }
        }
    }
    return isValid;
}

А вот как сделать более лаконично:

private bool ValidateUsername(string username)
{
    const int MinUsernameLength = 6;
    if (username.Length < MinUsernameLength) 
        return false;
    const int MaxUsernameLength = 25;
    if (username.Length > MaxUsernameLength) 
        return false;
    var isAlphaNumeric = username.All(Char.IsLetterOrDigit);
    if (isAlphaNumeric) 
        return false;
    if (!ContainsCurseWords(username)) 
        return false;
    return IsUniqueUsername(username);
}

Если может выпасть исколючение или ошибка, то пускай это произойдет как можно раньше:

public void RegisterUser(string username, string password)
{
    if (!string.IsNullOrWhiteSpace(username))
    {
        if (!string.IsNullOrWhiteSpace(password))
        {
            //регистрация пользователя
        }
        else
        {
            throw new ArgumentException("Username is required.");
        }
    }
    else
    {
        throw new ArgumentException("Password is required");
    }
}

И по традиции:

public void RegisterUser(string username, string password)
{
    if (!string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Username is required.");
    if (!string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Password is required");

    //регистрация пользователя
}

И на последок... Зная все и используя все эти знания, можно получить отличный код, но как же быть, если только в начале пути а дать подзатыльник некому? Ответ есть: Visual Studio любезно предоставила такое средство как Code Metrics.  Начать его использовать очень просто : всего лишь правой кнопокй мыши по "проекты" и "выбрать" Calculate Code Metrics. Более детально с результатами и значениями всего этого можно ознакомится вот здесь: Code Metrics

P.S. Пишите отличный код, прочитав статью, дай прочитать ее другу, а тот пусть своему другу передаст, и будет вам счастье, много счастья. И не будет у вас в проекте все так, как было в анекдоте в самом начале статьи.

d2funlife | Даниил Павлов 2015-2020
Powered by ASP.NET Core 2.2, Entity Framework Core 2.2. Web + UI