Проверка предусловий

Последнее обновление: 04.09.2023

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

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

void someFunction(char* user_input)
{
  if(user_input == NULL)
  {
    return;
  }
  operateOnData(user_input);
}

Подобный паттерн нередко называют Guard Clause. Применим его. И сначала рассмотрим следующую программу:

#include <stdio.h>

void writeTextToFile(char*, char*);

int main(void){
    char* filename ="test.txt";
    char* text = "hello";
    writeTextToFile(filename, text);
}

void writeTextToFile(char* file_name, char* text)
{
    FILE* file_pointer = 0;
    
    if(file_name) // если имя файла указано
    {
        // открываем файл
        if((file_pointer=fopen(file_name, "w")))    // если файл открыт
        { 
            if(text)    // если валидный текст
            {
                // запись в файл
                if(fputs(text, file_pointer)!=EOF)
                {
                    printf("Success! Text has been written to the file!\n");
                }
            }    
            else 
            {
                printf("Error! Text is incorrect!\n");
            }    
            // закрываем файл
            fclose(file_pointer);
        }
        else
        {
            printf("Error! Unable to open file!\n");
        }
    }
    else
    {
        printf("Error! File name is incorrect!\n");
    }
}

Функция writeTextToFile() записывает в файл строку. Название файла и записываемая строка передаются через параметры. То есть названия файла и записываемый текст формируют предусловия, от которых зависит выполнение функции. А именно и название файла, и текст не должны быть равны NULL. И функция проверяет эти предусловия. Но если мы взглянем на каскад ifов, то увидим, что проверка предусловий смешивается с основной логикой функции. Более того корректность зависиываемого текста вообще проверяется после открытия файла.

Теперь определим проверку предусловий отдельно:

#include <stdio.h>

void writeTextToFile(char*, char*);

int main(void){
    char* filename ="test.txt";
    char* text = "hello";
    writeTextToFile(filename, text);
}

void writeTextToFile(char* file_name, char* text)
{
    FILE* file_pointer = 0;
    
    // проверка предусловий 
    if(!file_name==NULL) // если имя файла не указано, выход из функции
    {
        printf("Error! File name is incorrect!\n");
        return;
    }
    if(text == NULL)    // если текст не указан
    {
        printf("Error! Text is incorrect!\n");
        return;
    }    
    // открываем файл
    if((file_pointer=fopen(file_name, "w")))    // если файл открыт
    { 
        // запись в файл
        if(fputs(text, file_pointer)!=EOF)
        {
            printf("Success! Text has been written to the file!\n");
        }   
        // закрываем файл
        fclose(file_pointer);
    }
    else
    {
        printf("Error! Unable to open file!\n");
    }
}

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

Но мы можем пойти дальше. На уровне основной части функции также есть предусловие - открыт файл или нет. Мы также можем подобным образом проверять предусловия к отдельным функциональным блокам функции:

#include <stdio.h>

void writeTextToFile(char*, char*);

int main(void){
    char* filename ="test.txt";
    char* text = "hello 2";
    writeTextToFile(filename, text);
}

void writeTextToFile(char* file_name, char* text)
{
    // проверка предусловий 
    if(file_name==NULL) // если имя файла не указано, выход из функции
    {
        printf("Error! File name is incorrect!\n");
        return;
    }
    if(text == NULL)    // если текст не указан
    {
        printf("Error! Text is incorrect!\n");
        return;
    }    
    // открываем файл
    FILE* file_pointer=fopen(file_name, "w");
    if(file_pointer == NULL)    // если открытие файла прошло неудачно, выход из функции открыт
    { 
        printf("Error! Unable to open file!\n");
        return;
    }
    // запись в файл
    if(fputs(text, file_pointer)!=EOF)
    {
        printf("Success! Text has been written to the file!\n");
    }   
    // закрываем файл
    fclose(file_pointer);
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850