Нередко выполнение функции зависит от некоторых предусловий. В качестве предусловий может выступать контекст, в котором запускается функция. В частности, это могут быть значения параметров или каких-то глобальных переменных. Соответственно проверка предусловий может быть очень важна, поскольку если предусловия не соблюдаются, то выполнение каких-то отдельных моментов функции или вообще всей функции не имеет смысла. Однако проблема частно заключается в том, что проверка предусловий смешивается с основным функционалом, что усложняем чтение и поддержку функции.
Общий механизм при работе с предусловиями состоит в их проверке в начале функции и немедленный выход из функции, если предусловия не соблюдаются, наподобие следующего:
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); }