Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Знание видеть абстракции

Anna | 25.06.2014 | нет комментариев

Моему сыну, как и многим мальчишкам, нравятся автомобили. Причём чем они огромнее и страннее — тем огромнее нравятся. Когда мы идём по улице, а мимо проезжает эвакуатор либо снегоуборочная машина, он постоянно дёргает меня за руку, указывает на заинтересовавший его объект и говорит: «Отец, б-р-р!». Говорит он так потому, что ему один год и вышеуказанные два слова составляют 40% его словарного резерва. Тем ни менее, в всеобщем мысль внятна — обратить внимание на автомобиль. Давайте подумаем, каким образом ребёнок в возрасте 8-10 лет сказал бы своему сверстнику то же самое. Что-то как бы «Ух ты, смотри какая крутая тачка!», да? Мысль та же, но обратите внимание — теснее шесть слов взамен 2-х. И, наконец, представьте, каким образом то же самое скажет человек лет в тридцать: «Эй, смотри, да это же Ferrari California 2008-го года выпуска с мотором V8 мощностью в 454 лошадиных силы и 7-ми скоростной коробкой-автоматом! Она до сотни разгоняется за 3.9 секунды!». Да, тут теснее огромнее деталей, но, если вы не автомеханик либо фанат Ferrari — они вам скорее каждого не необходимы и не главны. Основная же мысль — всё та же, что и в «Ух ты, смотри какая крутая тачка!» либо «Отец, б-р-р!». Но выражена она теснее в 30 слов.

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

О чём вообще идёт речь

По моему суждению, основная колляция классного программиста это не большие познания математики, не 100 лет навыка за плечами, не познания кучи языков и библиотек, не %куча_других_неважных_вещей%, а именно знание видеть абстракции. Не только видеть, безусловно, а ещё и проектировать, применять, исправлять и т.д. Но факт есть факт. Триумф какого-нибудь знаменитого сегодня продукта (подставьте сюда свою любимую ОС, браузер, игру — да что желательно) определён именно тем, насколько отлично спроектирована его зодчество, насколько отлично высокоуровневые части отделены от низкоуровневых и друг от друга.

Посмотрите на «усопшие» планы. Дюже редко они умирают от того, что программист не сумел на 10% повысить скорость работы, либо потому что не сумели прикрутить необходимую библиотеку. Почаще каждого повод закрытия формулируется в духе «присутствующая зодчество делает твердо немыслимым последующее становление». Вот она, оплошность в видении абстракций. Кто-то когда-то давным-давно не увидел, что несколько сущностей на самом деле являются одной, либо что одна может иметь несколько представлений, либо что на самом деле не заказчик должен дёргать сервер, а напротив, либо что в протокол хорошо бы заложить вероятность растяжения — и вот он, грянувший через годы гром последствий.

Паттерны

В современном мире программирования есть такая штука как «паттерны». Ну, знаете, книга шайки четырёх, каждые там фабрикисинглтоныобёрткинаблюдателифасадымосты. Отношение программистов к паттернам неоднозначно. Есть лагерь любителей паттернов, которые объективно утверждают, что это всё — суть десятилетий лучшего программерского навыка, проверенные вещи и нужно не тормозить, а применять наработки по полной. И есть лагерь противников паттернов, которые пеняют им на излишнюю трудность (3-5 классов на реализацию одной идеи — абсолютно нормально для средненького паттерна), говорят, что постижение паттернов аналогично школьной зубрёжке — когда легко учишь что-то без понимания причин, следствий и вариантов именно целевого применения.

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

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

Инструменты

К сожалению, современные средства разработки не дают отличных механических средств для того, Дабы видеть абстракции в плане. Да, вы можете увидеть интерфейсы либо абстрактные классы в коде — но не факт, что это то, что составляет настоящую абстракцию. Какой-то определённый слой логики может содержать в себе десятки интерфейсов, а иной — иметь каждого один класс (и тот — легко пустая обёртка вокруг чего-то иного). Мы можем с поддержкой IDE увидеть классы, способы и переменные — но мы не видим реального распределения плана на слои. Всё остаётся на совести программиста. К счастью, у нас сегодня есть вероятность переносить код в отдельные модули, у нас есть пространства имён, интерфейсы, дельные советы по рефакторингу и инструменты для его осуществления. Написать отлично разделённый на отдельные модули код — допустимо. И это главней, чем написать стремительный код. Безусловно, «модульный код» не на 100% равно «совершенный код», но дюже и дюже к этому близко.

Пример плохого кода

Несколько лет назад был пик популярности текстового редактора Notepad . Десятки миллионов загрузок, славный минималистичный интерфейс, плагины. Предисловие было дюже отменное, ничто не предвещало беды. За последние пару лет данный текстовый редактор сдулся и реально застопорился в своём становлении. Вот график его загрузок.

В чём же поводы? Я не берусь называть их все, но вот одна. Давайте посмотрим на один файл из его исходников.
NppBigSwitch.cpp

Внимательнее разберём некоторые части этого кода.

Недопонимание представления ‘имя переменной’.

Macro m = _macro;

Имя переменной — это не имя регистра процессора. Основная задача имени переменной не адресовать ячейку в памяти, а объяснить, что за данные в ней находятся. Легко для адресации дозволено было бы один раз в начале выделить массив в памяти и бегать по нему указателями. Именно имя переменной является той абстракцией, которая упрощает осознавание кода. Тут, как видим, не упрощает.

Недопонимание работы систем контроля версий

/*
		case NPPM_ADDREBAR :
		{
			if (!lParam)
				return FALSE;
			_rebarTop.addBand((REBARBANDINFO*)lParam, false);
			return TRUE;
		}

		case NPPM_UPDATEREBAR :
		{
			if (!lParam || wParam < REBAR_BAR_EXTERNAL)
				return FALSE;
			_rebarTop.reNew((int)wParam, (REBARBANDINFO*)lParam);
			return TRUE;
		}

		case NPPM_REMOVEREBAR :
		{
			if (wParam < REBAR_BAR_EXTERNAL)
				return FALSE;
			_rebarTop.removeBand((int)wParam);
			return TRUE;
		}
*/

Ветхому непотребному коду необходимо быть удалённым с комментарием в отдельном коммите о том отчего он был удалён. Если это ещё не дописанная функциональность — её место в отдельной ветке. Легко так удерживать нагромождения закомментированого кода в файлах плана — значит не понимать идеи систем контроля версий.

Непонимания того, где значимей скорость работы программы, а где – скорость работы программиста

LRESULT Notepad_plus::process(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
...
 // здесь 1800 строк кода
...
}

Ничего так размерчик для одной функции, да? Автор, вероятно, пытался сэкономить целый десяток наносекунд на вызов всякого отдельного ломтика кода через отдельную функцию. Молодец, сэкономил. И получил код, в котором отвратно копаться и необходимо тратить кучу времени на осознавание и исправление.

Непонимания абстракций ‘флаг’, ‘константа’, ‘магическое число’.

nmdlg->Items[i] = 0xFFFFFFFF; // indicate file was closed
...
if ((lParam == 1) || (lParam == 2))
...
long view = MAIN_VIEW;
view <<= 30;
return view|i;

Что такое 0xFFFFFFFF, 1, 2 и 30? Ах да, 0xFFFFFFFF — обозначает что файл был закрыт. Какой Удивительный комментарий, как всё наглядно! Было, видимо, дюже стремительно и комфортно легко набросать цифрыв код — компилируется чай. Нет времени пояснять, поехали дальше, да?

Неимение абстракции над кодировками

				case COPYDATA_FILENAMESA :
				{
					char *fileNamesA = (char *)pCopyData->lpData;
					CmdLineParams & cmdLineParams = pNppParam->getCmdLineParams();
#ifdef UNICODE
					WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance();
					const wchar_t *fileNamesW = wmc->char2wchar(fileNamesA, CP_ACP);
					loadCommandlineParams(fileNamesW, &cmdLineParams);
#else
					loadCommandlineParams(fileNamesA, &cmdLineParams);
#endif
					break;
				}

				case COPYDATA_FILENAMESW :
				{
					wchar_t *fileNamesW = (wchar_t *)pCopyData->lpData;
					CmdLineParams & cmdLineParams = pNppParam->getCmdLineParams();

#ifdef UNICODE
					loadCommandlineParams(fileNamesW, &cmdLineParams);
#else
					WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance();
					const char *fileNamesA = wmc->wchar2char(fileNamesW, CP_ACP);
					loadCommandlineParams(fileNamesA, &cmdLineParams);
#endif
					break;
				}
			}

            return TRUE;
        }

В всяком месте, где имеет значение в юникоде скомпилирована программа либо нет, мы видим конструкции, нагружающие мозг программиста непотребными в данный момент вещами. Даже в Win32 API, где многие функции имеют юникодную и неюникодную версии мы не думаем всякий раз что вызывать, легко пишем «MessageBox», а вследствие макросам это заменяется на MessageBoxA либо MessageBoxW. Это разрешает подняться над данным ярусом, отключить мозг от необходимости помнить об этой детали. Авторам Notepad такой путь, видимо, кажется слишком лёгким.

Неимение слоя абстракции над UI-примитивами операционной системы

::MoveWindow(_rebarTop.getHSelf(), 0, 0, rc.right, _rebarTop.getHeight(), TRUE);
...
::SendMessage(_statusBar.getHSelf(), WM_SIZE, wParam, lParam);

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

Как итог каждого вышеперечисленного, разработка Notepad является катастрофически трудным и неторопливым делом. Мой патч, исправляющий пару значимых для меня багов висит в списке «на рассмотрении» теснее полгода, совместно с примерно 200 другими патчами. Автор, безусловно, время от времени что-то из них принимает, но вы сами понимаете — делать это стремительно с такой кодовой базой безусловно немыслимо. Мне дюже жалко показавшийся мне когда-то отличным редактор, но вы видите сами — гибель неотвратима.

Пример отличного кода

Допустимо, вам знаменита такая знаменитая библиотека как Qt — о ней в последнее время много пишут на Прогре. Я, вновь-таки, не берусь утверждать, что знаю отменнее всех все поводы её триумфа, но вот вам одна из них. Каждая библиотека построена на очаровательном ядре абстракций: здесь есть абстракции от платформы, от сети, от элементов интерфейса ОС, от кодировок, да фактически от чего желательно. Взглянув на всякий компонент Qt нам не необходимо лезть крепко велико вниз либо вверх, Дабы осознать как он работает. Причём всё это не вследствие отличной документации, а из-за кода самой библиотеки.

Давайте посмотрим на один из заголовочных файлов библиотеки Qt.

qpdfwriter.h

#ifndef QPDFWRITER_H
#define QPDFWRITER_H

#include <QtCore/qobject.h>
#include <QtGui/qpagedpaintdevice.h>

QT_BEGIN_NAMESPACE

class QIODevice;
class QPdfWriterPrivate;

class Q_GUI_EXPORT QPdfWriter : public QObject, public QPagedPaintDevice
{
    Q_OBJECT
public:
    explicit QPdfWriter(const QString &filename);
    explicit QPdfWriter(QIODevice *device);
    ~QPdfWriter();

    QString title() const;
    void setTitle(const QString &title);

    QString creator() const;
    void setCreator(const QString &creator);

    bool newPage();

    void setPageSize(PageSize size);
    void setPageSizeMM(const QSizeF &size);

    void setMargins(const Margins &m);

protected:
    QPaintEngine *paintEngine() const;
    int metric(PaintDeviceMetric id) const;

private:
    Q_DISABLE_COPY(QPdfWriter)
    Q_DECLARE_PRIVATE(QPdfWriter)
};

QT_END_NAMESPACE

#endif

Не пугайтесь допустимо неизвестных вам макросов — не о них речь. Я хочу обратить ваше внимание на иную вещь. Подметьте, в этом классе нет приватных свойств. И примерно ни в одном ином классе Qt — тоже нет. Точнее, на самом деле в всяком из них есть ровно по одной приватной переменной — это неявно объявленный через макрос Q_DECLARE_PRIVATE указатель на подкласс, в котором теснее и находятся все свойства и часть приватных способов. Сделано это по официальному объяснению для обеспечения бинарной совместимости — получается что данный класс в всякий версии Qt имеет один и тот же размер (от того что место для хранения одного указателя константно в пределах платформы) и его дозволено спокойно передавать туда-сюда между модулями, не опасаясь каких-нибудь там segmentation fault.

На самом деле для меня каждая прелесть этого решения в ином. Глядите — мы открываем заголовочный файл и что же мы видим? Только публичные способы. Вы впервой видите данный заголовочный файл (да и вообще библиотеку Qt, может быть) — но вы чай теснее осознали, что это за класс и как его применять, правда? Все внутренности изысканно спрятаны в приватном подклассе. К сожалению, типичный С принуждает программиста смешивать в заголовочном файле и то, что необходимо внешнему пользователю класса и его внутренности. Но в Qt при чтении заголовочного файла — мы видим чёткое послание от разработчиков Qt: «Внутренности этого класса тебе не необходимы. Не твоё дело что там и как, это абстракция — используй её через публичные способы». И это восхитительно, чёрт возьми! Я хочу видеть все библиотеки в сходственном жанре. Покажите мне надобные мне вещи сразу и спрячьте непотребные так, Дабы их необходимо было искать длинно и сложно. За этим подходом грядущее, я уверен.

Итоги

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

Славного программирования.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB