За черную консоль замолвите слово..
Существует популярное мнение что консольный интерфейс — архаичен, скоро вымрет и вообще не нужен. Соответственно дни консольных программ сочтены и пытаться создавать новые в нашем светлом 21 веке точно не стоит.
А будущее выглядит как-то так:
Загадывать на будущее — дело неблагодарное, поэтому предсказывать развитие событий не берусь.
Тем не менее опишу важные особенности консоли и текстовых интерфейсов, которые позволят использовать их еще долгие долгие годы, если не десятилетия. Думаю что и вас и меня консольный терминал переживет.
Круглое как шар, надежное как лом
Шанс сломать консольную программу, а тем более саму консоль действиями пользователя примерно равен шансу сломать металлический шар.
То есть это конечно возможно технически, но:
Победителям — кто смог дают белый билет медаль и записывают в пентестеры. Или оскопляют публично ломают ноги, как повезет.
Вот так выглядит код простейшей консольной программы на Си:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { char str[20]; printf("Строку введи да: "); fgets(str, 20, stdin); printf("%s\n", str); return 0; }
Все что она делает это ожидает ввода и затем отображает введенную строку:
Вся логика работы строго последовательная, как и у большинства консольных программ.
printf("Строку введи да: ");
fgets(str, 20, stdin);
printf("%s\n", str);
Все максимально просто и однозначно.
Поэтому большую часть исходного кода консольной программы всегда будет составлять прикладная логика:
алгоритмы, расчеты, обработка данных и так далее.
Та самая бизнес-логика, за реализацию которой платят деньги.
Теперь тоже самое, но с графическим интерфейсом (С++ и Qt):
#include <QApplication> #include <QLabel> #include <QWidget> #include <QLineEdit> #include <QVBoxLayout> class МойВиджет : public QWidget{ private: QLabel* вывод; QLineEdit* вводТекст; public: МойВиджет(QWidget *parent = NULL) : QWidget(parent) { this->resize(320, 40); this->setWindowTitle("Страх и ужос"); QVBoxLayout *layout = new QVBoxLayout( this ); layout->setMargin( 0 ); QLabel* введиМеня = new QLabel("Введи текст да:", this); layout->addWidget( введиМеня ); вводТекст = new QLineEdit( this ); вводТекст->setPlaceholderText("вот сюда"); вводТекст->setFocus(); layout->addWidget( вводТекст ); вывод = new QLabel("", this); layout->addWidget( вывод ); connect(вводТекст, &QLineEdit::textChanged, this, &МойВиджет::customSlot); } void customSlot() { вывод->setText(вводТекст->text()); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); МойВиджет widget; widget.show(); return app.exec(); }
Размер кода как видите сразу утроился.
Не надо фокусироваться на QT или разнице между Си и C++, неважен язык или графический фреймворк:
код графического интерфейса в любом варианте будет объемнее и сложнее чем у консольной программы, на любых технологиях и платформах.
Теперь давайте более детально разберем второй пример, я постарался сделать его максимально наглядным.
Посмотрим, что там внутри есть.
Графические примитивы
При любом испольовании графического интерфейса, мы обязательно будем иметь дело с графическими примитивами. Их много и они разные.
#include <QLabel> #include <QWidget> #include <QLineEdit> #include <QVBoxLayout>
Да, как вы догадались, все это — объекты, поэтому графический интерфейс это всегда ООП, пусть хоть в извращенном виде (привет Cocoa), но все-таки ООП.
Работая с интерфейсом — вы всегда работаете с объектами.
Хоть в функциональном стиле, с лямбдами:
#! /usr/local/bin/wish8.5 button .b -text 0 -command {.b config -text [expr {[.b cget -text]+1}]} pack .b
Хоть через struct в кристально чистом Си:
window = gtk_application_window_new (app); gtk_window_set_title (GTK_WINDOW (window), "Window"); gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); button = gtk_button_new_with_label ("Hello World"); g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); gtk_window_set_child (GTK_WINDOW (window), button);
Общий подход к работе будет все равно из ООП.
Сложная логика настройки и связывания
Имея дело с графическим интерфейсом, в проекте всегда будет огромная жопа гора кода, отвечающая за его настройку:
связывание и расположение элементов, размеры, отступы, раскраска и так далее.
В примере выше это выглядит вот так:
this->resize(320, 40); .. layout->setMargin( 0 ); .. layout->addWidget( вводТекст ); .. вводТекст->setFocus(); ..
Вот тут более объемный пример из официальной документации.
Даже по эталонному примеру видно, что кода отвечающего за настройки очень и очень много. И избежать этого не получится.
А вам нужно работать и решать прикладные задачи вашим софтом.
Поэтому далеко не всегда такие затраты на создание интерфейса осмысленны и разумны в экономическом плане.
События
connect(вводТекст, &QLineEdit::textChanged, this, &МойВиджет::customSlot); } void customSlot() { вывод->setText(вводТекст->text()); }
Графический интерфейс подразумевает интерактивное взаимодействие с пользователем:
движение мышкой, клики, перетаскивания блоков, скрытие-раскрытие окон и так далее.
Именно для этого его придумали и развивали.
Вся работа происходит через определенные паттерны, каждый паттерн это одновременно и ввод и вывод.
Вот так это начиналось:
Хороший пример это перетаскивание мышкой окна:
навели курсор на шапку окна, зажали мышью и потащили. Пока тащите - окно визуально перемещается по экрану, т.е. работает и ввод и вывод.
Теперь представьте сложность происходящего:
расчет перекрывающихся окон, частичная отрисовка содержимого каждого окна с учетом перекрытий, сама реакция на событие: перетаскивание работает с зажатой кнопкой мыши и останавливается когда вы отпускаете кнопку.
Не обосраться при разработки софта с графическим интерфейсом очень и очень сложно, даже если вы большая компания с бесконечными ресурсами.
Поэтому заканчивается временами вот так:
Проблемы
Так что мы получим в обязательном порядке, связавшись с графическим интерфейсом?
Проблемы конечно, кучу проблем.
Память, которая обязательно будет течь.
Обеспечить работу программы с графическим интерфейсом без механизма автоматического управления памятью - нереально экстремально сложно.
Поэтому графический интерфейс подразумевает скачки потребления ресурсов, которые временами заканчиваются:
Все графическое и интерактивное - течет.
Риск ошибки
Сама концепция графического интерактивного интерфейса подразумевает несколько разных вариантов ввода:
клавиатура и мышь или разнообразные движения пальцем в случае планшета
текст, графика, анимация, звуки
И все это работает одновременно. Естественно это порождает ошибки:
перегрузка ввода, утеря событий, пропуск кадров анимации — это все обязательная обратная сторона любого графического интерфейса.
чем больше кода в вашем проекте тем больше шанс на ошибки в нем.
Существуют даже исследования, изучающие соотношение между объемом кода, уровнем программиста и количеством ошибок, которые он делает в день:
https://en.wikipedia.org/wiki/Software_metric
https://en.wikipedia.org/wiki/Software_bug#Benchmark_of_bugs
И надо сказать что соотношение там точно не в пользу ПО с графическим интерфейсом.
Хотя это еще далеко не все, на одном обьеме ваши проблемы на пути в графический рай не заканчиваются.
Контроль ввода
Дело в том что окружение программы с графическим интерфейсом — тоже графическое (сюрприз). А значит содержит все те же сложности и ошибки в реализации, тестировании и тд, только умноженные на сто: окружению еще нужно уметь контролировать ваши программы.
Вот что бывает когда у окружения это не получается:
Не грози консольному централу, возюкая мышкой в своем интерфейсе
За 40 лет развития графических интерфейсов, с появления Apple Lisa и до нынешних 3D-шлемов с виртуальной реальностью, фундаментальные проблемы графического интерфейса не то что не были решены — они умножились.
И то что подавалось и продавалось нам как однозначный прогресс и хайтек, одновременно создало вокруг индустрии ПО ареол ненадежности.
Слышим «программный» подразумеваем «ненадежный».
Глюк, баг, зависнуть — это теперь часть лексикона даже очень далеких от компьютеров людей.
Можете проверить сами, попросив ваших пожилых родителей описать смысл фразы «компьютер завис» — результат удивит.
Вообщем единственным реальным вариантом создания чего-то по-настоящему надежного является разработка консольного ПО с текстовым интерфейсом.